Redux: Альтернативный подход к асинхронным действиям

Созданный на 27 дек. 2015  ·  44Комментарии  ·  Источник: reduxjs/redux

Я изучал альтернативу способу выполнения асинхронных действий в redux, и я был бы признателен за любые комментарии, которые могут быть у других о том, что я сделал.

Чтобы проиллюстрировать свой подход, я изменил пример async в моем клоне redux: https://github.com/winstonewert/redux/tree/master/examples/async

Обычно внешние действия выполняются асинхронными создателями действий. В случае примера async создатель действия fetchPosts отправляет действие REQUEST_POSTS, чтобы указать начало запроса, за которым следует RECEIVE_POSTS после того, как сообщения вернулись из api.

В моем примере все создатели действий синхронны. Вместо этого есть функция, которая возвращает список асинхронных действий, которые в настоящее время должны выполняться в зависимости от состояния. См. Мой пример здесь: https://github.com/rackt/redux/compare/master...winstonewert : master # diff-8a94dc7aa7bdc6e5390c9216a69761f8R12

Функция doReactions подписывается на хранилище и гарантирует, что фактическое состояние выполняемых в настоящее время запросов совпадает с состоянием, возвращаемым состоянием doReactions, путем запуска или отмены запросов.

Так в чем разница?

1) Функция реакции - это чистая функция состояния. Это упрощает тестирование.
2) Фактическая логика того, какие запросы делать, проще. Посмотрите на несколько строковых функций в моем примере в сравнении с различными частями логики, распространенными ранее через контейнеры и создатели действий.
3) Облегчает отмену запросов.

Есть предположения?

discussion feedback wanted

Самый полезный комментарий

Я тоже много думал об альтернативных способах обработки побочных эффектов в redux, и я надеюсь, что не украду ваш поток, поскольку я вываливаю некоторые из проблем, которые я вижу с некоторыми текущими подходами, и почему и как я думаю, что это огромный шаг в правильном направлении, несмотря на кажущуюся простоту.

Проблема с побочными эффектами в создателях действий

В чисто функциональных языках побочные эффекты всегда переносятся на край приложения и возвращаются в среду выполнения для выполнения. В Elm редукторы возвращают кортеж, содержащий новое состояние и любые эффекты, которые должны быть выполнены. Однако методы с этой сигнатурой еще не могут быть объединены с другими редукторами redux.

Очевидным (но, возможно, не лучшим) местом для выполнения побочных эффектов в redux стали создатели действий, и для поддержки этого шаблона было разработано несколько различных вариантов промежуточного программного обеспечения. Тем не менее, я думаю, что нынешние подходы к промежуточному программному обеспечению - это скорее обходной путь для невозможности возвращать побочные эффекты в качестве первоклассной концепции редукторов.

Хотя люди по-прежнему создают потрясающие вещи с помощью redux, и это большой шаг вперед, намного проще и прагматичнее, чем большинство альтернатив, я вижу несколько проблем, связанных с побочными эффектами внутри создателей действий:

  • Неявное состояние скрыто
  • Дублирование бизнес-логики
  • Допущения и / или зависимости контекста снижают возможность повторного использования
  • Создатели действий с побочными эффектами сложно тестировать
  • Невозможно оптимизировать или объединить в пакет

Неявное состояние скрыто

В счетчике приложение incrementAsync создает тайм-аут, и только по его завершении обновляется состояние приложения. Если вы хотите, например, отобразить визуальный индикатор того, что выполняется операция приращения, представление не может вывести это из состояния приложения. Это состояние неявное и скрытое.

Хотя иногда это элегантно, я не уверен в предложении использовать генераторы в качестве оркестраторов создателей действий, поскольку неявное состояние скрыто и не может быть легко сериализовано.

Используя redux-thunk или подобное, вы можете отправить несколько сообщений редуктору, информируя его о том, когда операция приращения началась и когда она завершилась, но это создает другой набор проблем.

Перемотка состояния к точке, где операция приращения помечена как выполняющаяся после завершения эффекта, на самом деле не будет регенерировать побочный эффект и, таким образом, будет оставаться в процессе неопределенно долго.

Ваше предложение, похоже, решает эту проблему. Поскольку побочные эффекты создаются из состояния, намерение должно быть выражено результирующим состоянием в той или иной форме, таким образом, если кто-то вернет хранилище в предыдущее состояние, в котором инициировано действие, то реакции будут инициировать эффект снова, а не оставить состояние в подвешенном состоянии.

Дублирование бизнес-логики

Естественно, что действия вызывают побочный эффект только тогда, когда приложение находится в определенном состоянии. В redux, если создатель действия требует состояния, он должен быть простой и чистой функцией или явно предоставлен с состоянием.

В качестве простого примера допустим, что мы начинаем с примера приложения счетчика и хотим менять счетчик на случайный цвет шрифта каждый раз, когда счетчик кратен 5.

Поскольку генерация случайных чисел является нечистой, рекомендуется поместить это поведение в создатель действий. Однако есть несколько различных действий, которые могут изменить значение счетчика: increment, декремент, incrementAsync, incrementIfOdd (которые в этом случае изменять не нужно).

Инкремент и декремент ранее не требовали какого-либо состояния, поскольку они ранее обрабатывались в редукторе и, таким образом, имели доступ к текущему значению, но поскольку редуктор не может иметь или возвращать побочные эффекты (генерация случайных чисел), эти функции теперь становятся нечистыми создателями действий, которые нуждаются в чтобы узнать текущее значение счетчика, чтобы определить, нужно ли выбирать новый случайный цвет шрифта, и эту логику необходимо продублировать во всех создателях встречных действий.

Одной из возможных альтернатив явному предоставлению текущего состояния было бы использование redux-thunk и возврат обратного вызова для доступа к текущему состоянию. Это позволяет избежать изменения всех мест, где создаются действия для предоставления текущего значения, но теперь требуется, чтобы создатель действия знал, где в глобальном состоянии приложения хранится значение, и это ограничивает возможность многократного использования одного и того же счетчика в пределах в одном приложении или в разных приложениях, где состояние может быть по-разному структурировано.

Допущения и / или зависимости контекста снижают возможность повторного использования

Снова вернувшись к примеру счетчика, вы заметите, что существует только один экземпляр счетчика. Хотя тривиально иметь много счетчиков на странице, которые просматривают / обновляют одно и то же состояние, требуются дополнительные модификации счетчика, если вы хотите, чтобы каждый счетчик использовал другое состояние.

Это уже обсуждалось ранее. Как создать общий список в качестве редуктора и усилителя компонентов?

Если бы счетчик использовал только простые типы действий, было бы относительно тривиально применить архитектуру вяза .

В этом случае родительский элемент просто обертывает создателей действий или диспетчер, чтобы дополнить сообщение любым необходимым контекстом, а затем он может напрямую вызвать редуктор с локализованным состоянием.

Хотя пример React Elmish выглядит впечатляюще, в нем заметно не хватает двух создателей проблемных действий, incrementIfOdd и incrementAsync.

incrementIfOdd зависит от промежуточного программного обеспечения для определения текущего состояния и, следовательно, должен знать свое местоположение в состоянии приложения.

incrementAsync в конечном итоге напрямую отправляет действие приращения, которое не предоставляется родительскому компоненту и, следовательно, не может быть обернуто дополнительным контекстом.

Хотя ваше предложение напрямую не решает эту проблему, если incrementAsync был реализован как простое действие, которое изменило состояние на {counter: 0, incrementAfterDelay: 1000} для запуска побочного эффекта в прослушивателе хранилища, то incrementAsync станет простым сообщением. incrementIfOdd является чистым, поэтому он может быть либо реализован в редукторе, либо может быть явно предоставлен для состояния .... Таким образом, появляется возможность снова применить архитектуру elm, если это необходимо.

Создатели действий с побочными эффектами сложно тестировать

Я думаю, что это довольно очевидно, что побочные эффекты будет труднее тестировать. Как только ваши побочные эффекты становятся зависимыми от текущего состояния и бизнес-логики, их становится не только сложнее, но и важнее тестировать.

Ваше предложение позволяет легко создать тест, согласно которому переход состояния создает состояние, содержащее желаемые реакции, без фактического выполнения какой-либо из них. Реакции также легче тестировать, поскольку они не нуждаются в условном состоянии или бизнес-логике.

Невозможно оптимизировать или объединить в пакет

В недавнем сообщении в блоге Джона Э. Де Гуса обсуждалась проблема непрозрачных типов данных, таких как IO или Task, для выражения эффектов. Используя декларативные описания побочных эффектов, а не непрозрачные типы, вы можете позже оптимизировать или комбинировать эффекты.

Современная архитектура для FP

Преобразователи, обещания и генераторы непрозрачны, поэтому такие оптимизации, как пакетирование и / или подавление повторяющихся вызовов API, должны выполняться явно с помощью функций, подобных fetchPostsIfNeeded .

Ваше предложение исключает fetchPostsIfNeeded и кажется вполне возможным реализовать функцию reactions которая могла бы оптимизировать несколько запросов и / или использовать другой набор API по мере необходимости, когда было запрошено больше или меньше данных.

Моя реализация

Недавно я создал форк redux, который позволяет создавать редукторы, которые возвращают только новое состояние, как сейчас, или специальный объект withEffects, содержащий новое состояние и описание любых эффектов, выполняемых после редуктора.

Я не был уверен, как это сделать без разветвления redux, поскольку было необходимо изменить compose и combReducers, чтобы поднять эффекты над существующими редукторами, чтобы сохранить совместимость с существующим кодом редуктора.

Однако ваше предложение довольно хорошее, поскольку не требует изменения редукции. Кроме того, я думаю, что ваше решение лучше справляется с проблемой подразумеваемого скрытого состояния и, вероятно, его легче комбинировать или оптимизировать результирующие реакции.

Резюме

Подобно тому, как React - это «просто пользовательский интерфейс» и не очень предписывает, как на самом деле хранить или обновлять состояние приложения, Redux в основном «просто хранилище» и не очень предписывает, как обрабатывать побочные эффекты.

Я никогда никого не виню за прагматичность и умение делать вещи, а многочисленные участники redux и промежуточного программного обеспечения позволяют людям создавать действительно крутые вещи быстрее и лучше, чем это было возможно раньше. Мы дошли так далеко только благодаря их вкладам. Особая благодарность всем, кто внес свой вклад.

Redux потрясающий. Это не обязательные проблемы с самим Redux, но, надеюсь, конструктивная критика текущих архитектурных паттернов, а также мотивы и потенциальные преимущества для запуска эффектов после, а не до изменений состояния.

Все 44 Комментарий

Интересный подход! Похоже, что в идеале он должен быть отделен от природы асинхронности, например, от метода, используемого для запуска XHR, или даже от того, что веб-запрос является источником асинхронности в первую очередь.

Я тоже много думал об альтернативных способах обработки побочных эффектов в redux, и я надеюсь, что не украду ваш поток, поскольку я вываливаю некоторые из проблем, которые я вижу с некоторыми текущими подходами, и почему и как я думаю, что это огромный шаг в правильном направлении, несмотря на кажущуюся простоту.

Проблема с побочными эффектами в создателях действий

В чисто функциональных языках побочные эффекты всегда переносятся на край приложения и возвращаются в среду выполнения для выполнения. В Elm редукторы возвращают кортеж, содержащий новое состояние и любые эффекты, которые должны быть выполнены. Однако методы с этой сигнатурой еще не могут быть объединены с другими редукторами redux.

Очевидным (но, возможно, не лучшим) местом для выполнения побочных эффектов в redux стали создатели действий, и для поддержки этого шаблона было разработано несколько различных вариантов промежуточного программного обеспечения. Тем не менее, я думаю, что нынешние подходы к промежуточному программному обеспечению - это скорее обходной путь для невозможности возвращать побочные эффекты в качестве первоклассной концепции редукторов.

Хотя люди по-прежнему создают потрясающие вещи с помощью redux, и это большой шаг вперед, намного проще и прагматичнее, чем большинство альтернатив, я вижу несколько проблем, связанных с побочными эффектами внутри создателей действий:

  • Неявное состояние скрыто
  • Дублирование бизнес-логики
  • Допущения и / или зависимости контекста снижают возможность повторного использования
  • Создатели действий с побочными эффектами сложно тестировать
  • Невозможно оптимизировать или объединить в пакет

Неявное состояние скрыто

В счетчике приложение incrementAsync создает тайм-аут, и только по его завершении обновляется состояние приложения. Если вы хотите, например, отобразить визуальный индикатор того, что выполняется операция приращения, представление не может вывести это из состояния приложения. Это состояние неявное и скрытое.

Хотя иногда это элегантно, я не уверен в предложении использовать генераторы в качестве оркестраторов создателей действий, поскольку неявное состояние скрыто и не может быть легко сериализовано.

Используя redux-thunk или подобное, вы можете отправить несколько сообщений редуктору, информируя его о том, когда операция приращения началась и когда она завершилась, но это создает другой набор проблем.

Перемотка состояния к точке, где операция приращения помечена как выполняющаяся после завершения эффекта, на самом деле не будет регенерировать побочный эффект и, таким образом, будет оставаться в процессе неопределенно долго.

Ваше предложение, похоже, решает эту проблему. Поскольку побочные эффекты создаются из состояния, намерение должно быть выражено результирующим состоянием в той или иной форме, таким образом, если кто-то вернет хранилище в предыдущее состояние, в котором инициировано действие, то реакции будут инициировать эффект снова, а не оставить состояние в подвешенном состоянии.

Дублирование бизнес-логики

Естественно, что действия вызывают побочный эффект только тогда, когда приложение находится в определенном состоянии. В redux, если создатель действия требует состояния, он должен быть простой и чистой функцией или явно предоставлен с состоянием.

В качестве простого примера допустим, что мы начинаем с примера приложения счетчика и хотим менять счетчик на случайный цвет шрифта каждый раз, когда счетчик кратен 5.

Поскольку генерация случайных чисел является нечистой, рекомендуется поместить это поведение в создатель действий. Однако есть несколько различных действий, которые могут изменить значение счетчика: increment, декремент, incrementAsync, incrementIfOdd (которые в этом случае изменять не нужно).

Инкремент и декремент ранее не требовали какого-либо состояния, поскольку они ранее обрабатывались в редукторе и, таким образом, имели доступ к текущему значению, но поскольку редуктор не может иметь или возвращать побочные эффекты (генерация случайных чисел), эти функции теперь становятся нечистыми создателями действий, которые нуждаются в чтобы узнать текущее значение счетчика, чтобы определить, нужно ли выбирать новый случайный цвет шрифта, и эту логику необходимо продублировать во всех создателях встречных действий.

Одной из возможных альтернатив явному предоставлению текущего состояния было бы использование redux-thunk и возврат обратного вызова для доступа к текущему состоянию. Это позволяет избежать изменения всех мест, где создаются действия для предоставления текущего значения, но теперь требуется, чтобы создатель действия знал, где в глобальном состоянии приложения хранится значение, и это ограничивает возможность многократного использования одного и того же счетчика в пределах в одном приложении или в разных приложениях, где состояние может быть по-разному структурировано.

Допущения и / или зависимости контекста снижают возможность повторного использования

Снова вернувшись к примеру счетчика, вы заметите, что существует только один экземпляр счетчика. Хотя тривиально иметь много счетчиков на странице, которые просматривают / обновляют одно и то же состояние, требуются дополнительные модификации счетчика, если вы хотите, чтобы каждый счетчик использовал другое состояние.

Это уже обсуждалось ранее. Как создать общий список в качестве редуктора и усилителя компонентов?

Если бы счетчик использовал только простые типы действий, было бы относительно тривиально применить архитектуру вяза .

В этом случае родительский элемент просто обертывает создателей действий или диспетчер, чтобы дополнить сообщение любым необходимым контекстом, а затем он может напрямую вызвать редуктор с локализованным состоянием.

Хотя пример React Elmish выглядит впечатляюще, в нем заметно не хватает двух создателей проблемных действий, incrementIfOdd и incrementAsync.

incrementIfOdd зависит от промежуточного программного обеспечения для определения текущего состояния и, следовательно, должен знать свое местоположение в состоянии приложения.

incrementAsync в конечном итоге напрямую отправляет действие приращения, которое не предоставляется родительскому компоненту и, следовательно, не может быть обернуто дополнительным контекстом.

Хотя ваше предложение напрямую не решает эту проблему, если incrementAsync был реализован как простое действие, которое изменило состояние на {counter: 0, incrementAfterDelay: 1000} для запуска побочного эффекта в прослушивателе хранилища, то incrementAsync станет простым сообщением. incrementIfOdd является чистым, поэтому он может быть либо реализован в редукторе, либо может быть явно предоставлен для состояния .... Таким образом, появляется возможность снова применить архитектуру elm, если это необходимо.

Создатели действий с побочными эффектами сложно тестировать

Я думаю, что это довольно очевидно, что побочные эффекты будет труднее тестировать. Как только ваши побочные эффекты становятся зависимыми от текущего состояния и бизнес-логики, их становится не только сложнее, но и важнее тестировать.

Ваше предложение позволяет легко создать тест, согласно которому переход состояния создает состояние, содержащее желаемые реакции, без фактического выполнения какой-либо из них. Реакции также легче тестировать, поскольку они не нуждаются в условном состоянии или бизнес-логике.

Невозможно оптимизировать или объединить в пакет

В недавнем сообщении в блоге Джона Э. Де Гуса обсуждалась проблема непрозрачных типов данных, таких как IO или Task, для выражения эффектов. Используя декларативные описания побочных эффектов, а не непрозрачные типы, вы можете позже оптимизировать или комбинировать эффекты.

Современная архитектура для FP

Преобразователи, обещания и генераторы непрозрачны, поэтому такие оптимизации, как пакетирование и / или подавление повторяющихся вызовов API, должны выполняться явно с помощью функций, подобных fetchPostsIfNeeded .

Ваше предложение исключает fetchPostsIfNeeded и кажется вполне возможным реализовать функцию reactions которая могла бы оптимизировать несколько запросов и / или использовать другой набор API по мере необходимости, когда было запрошено больше или меньше данных.

Моя реализация

Недавно я создал форк redux, который позволяет создавать редукторы, которые возвращают только новое состояние, как сейчас, или специальный объект withEffects, содержащий новое состояние и описание любых эффектов, выполняемых после редуктора.

Я не был уверен, как это сделать без разветвления redux, поскольку было необходимо изменить compose и combReducers, чтобы поднять эффекты над существующими редукторами, чтобы сохранить совместимость с существующим кодом редуктора.

Однако ваше предложение довольно хорошее, поскольку не требует изменения редукции. Кроме того, я думаю, что ваше решение лучше справляется с проблемой подразумеваемого скрытого состояния и, вероятно, его легче комбинировать или оптимизировать результирующие реакции.

Резюме

Подобно тому, как React - это «просто пользовательский интерфейс» и не очень предписывает, как на самом деле хранить или обновлять состояние приложения, Redux в основном «просто хранилище» и не очень предписывает, как обрабатывать побочные эффекты.

Я никогда никого не виню за прагматичность и умение делать вещи, а многочисленные участники redux и промежуточного программного обеспечения позволяют людям создавать действительно крутые вещи быстрее и лучше, чем это было возможно раньше. Мы дошли так далеко только благодаря их вкладам. Особая благодарность всем, кто внес свой вклад.

Redux потрясающий. Это не обязательные проблемы с самим Redux, но, надеюсь, конструктивная критика текущих архитектурных паттернов, а также мотивы и потенциальные преимущества для запуска эффектов после, а не до изменений состояния.

Я пытаюсь понять разницу между этим подходом и redux-saga. Меня интересует утверждение, что он неявно скрывает состояние в генераторах, потому что сначала кажется, что он делает то же самое. Но я полагаю, что это может зависеть от того, как реализовано io.take . Если сага будет обрабатывать действие только в том случае, если оно в настоящее время заблокировано на этом yield , тогда я определенно понимаю, что вы имеете в виду. Но если redux-saga ставит в очередь действия так, что io.take вернет прошлые действия, похоже, что он делает то же самое. В любом случае у вас есть логика, которая может асинхронно выполнять dispatch действия, запускаемые при прослушивании потока действий.

Однако это интересная концепция. Концептуализация Redux как потока действий, из которого запускаются переходы между состояниями и эффекты. Мне кажется, что это альтернативная точка зрения, нежели рассмотрение этого исключительно как процессора состояний.

В модели источников событий, я думаю, все сводится к тому, являются ли действия Redux «командами» (условные запросы на выполнение действия) или «событиями» (атомарные переходы состояния, отраженные в плоском представлении). Думаю, у нас есть инструмент, достаточно гибкий, чтобы о нем можно было думать в любом случае.

Я тоже немного недоволен статус-кво «создателей умных действий», но я подходил к нему по-другому, в котором Redux - это скорее хранилище событий, где действия являются одним из многих возможных эффектов, которые может быть запущен каким-то внешним слоем «контроллера». Я факторизовал код, который следовал этому подходу, в response-redux-controller , хотя у меня есть недоработанная идея о потенциально более легком способе достижения этой цели. Однако для этого потребовалось бы, чтобы у react-redux был хук, которого у него в настоящее время нет, и некоторые шутки об упаковке магазина, которые я не совсем проработал.

Описание магазина hijinks https://github.com/rackt/redux/issues/1200

Я пытаюсь понять разницу между этим подходом и redux-saga

Я не видел саги о сокращении до тех пор, пока не придумал свой подход, но определенно есть некоторые сходства. Но у меня все же есть отличия:

  1. Мой подход не имеет доступа к потоку действий, только к состоянию. redux-saga может запустить процесс просто потому, что произошло действие. Мой подход требует, чтобы редуктор изменил состояние, которое запускает функцию реакции для запроса действия.
  2. Мой подход требует, чтобы все состояния существовали в состоянии redux. Redux-saga имеет дополнительное состояние, которое живет в генераторе саги (в какой строке оно находится, значения локальных переменных).
  3. Мой подход изолирует асинхронную часть. Фактическую логику реакции можно проверить, не касаясь асинхронной функции. Сага сводит их вместе.
  4. Сага объединяет разные части одной и той же логики. Мой подход заставляет вас разбивать сагу на части, которые относятся к редуктору, реакциям и реализации типа реакции.

По сути, мой подход подчеркивает чистые функции и сохранение всего в состоянии редукции. Подход redux-saga подчеркивает важность большей выразительности. Думаю, есть плюсы и минусы, но мой мне больше нравится. Но я пристрастен.

Звучит многообещающе. Я думаю, что было бы более убедительно увидеть пример, который отделяет механизм реакции от логики предметной области.

Ваше предложение исключает fetchPostsIfNeeded, и кажется вполне возможным реализовать функцию реакции, которая могла бы оптимизировать несколько запросов и / или использовать другой набор API по мере необходимости, когда было запрошено больше или меньше данных.

В нынешнем виде вы не можете этого сделать в функции реакции. Там логика должна знать, какие действия уже запущены (мы не можем ничего добавить в них), но функция реакции не имеет информации. Механизм реакций, который использует функцию Reaction (), безусловно, мог бы делать эти вещи.

Я думаю, что было бы более убедительно увидеть пример, который отделяет механизм реакции от логики предметной области.

Я полагаю, вы имеете в виду способ, которым функция doReactions () обрабатывает запуск / остановку XMLHttpRequest? Я исследовал разные способы сделать это. Проблема в том, что трудно найти общий способ определить, являются ли две реакции одной и той же реакцией. IsEqual в Lodash почти работает, но не закрывается.

Я полагаю, вы имеете в виду способ, которым функция doReactions () обрабатывает запуск / остановку XMLHttpRequest?

Нет, я просто имею в виду, что в вашем примере вся конфигурация для настройки концепции реакции смешана с логикой предметной области, определяющей, какие данные извлекаются, а также с деталями того, как эти данные извлекаются. Мне кажется, что общие аспекты следует выделить во что-то, что меньше связано с деталями, характерными для этого примера.

Нет, я просто имею в виду, что в вашем примере вся конфигурация для настройки концепции реакции смешана с логикой предметной области, определяющей, какие данные извлекаются, а также с деталями того, как эти данные извлекаются. Мне кажется, что общие аспекты следует выделить во что-то, что меньше связано с деталями, характерными для этого примера.

Хм ... Я думаю, что мы не можем иметь в виду одно и то же, говоря о логике предметной области.

На мой взгляд, функция response () инкапсулирует логику предметной области и отделена от функции doReactions (), которая обрабатывает логику применения реакций. Но вы, кажется, имеете в виду другое ...

В нынешнем виде вы не можете этого сделать в функции реакции. Там логика должна знать, какие действия уже запущены (мы не можем ничего добавить в них), но функция реакции не имеет информации. Механизм реакций, который использует функцию Reaction (), безусловно, мог бы делать эти вещи.

В основном я имел в виду, что если одно событие вызвало изменение состояния, при котором несколько компонентов запрашивали одну и ту же информацию, тогда он мог бы их оптимизировать. Однако вы правы, что одного этого недостаточно, чтобы определить, ожидает ли еще побочный эффект от предыдущего изменения состояния, и, следовательно, дополнительный запрос не нужен.

Сначала я думал, что, возможно, можно сохранить все состояния в состоянии приложения, но когда я начал думать о недавней проблеме с секундомером, я понял, что хотя тот факт, что секундомер isOn должен храниться в состоянии приложения, фактический interval объект, связанный с этим секундомером, необходимо сохранить в другом месте. isOn должен находиться в состоянии приложения, но не только в этом случае.

В основном я имел в виду, что если одно событие вызвало изменение состояния, при котором несколько компонентов запрашивали одну и ту же информацию, тогда он мог бы их оптимизировать. Однако вы правы, что одного этого недостаточно, чтобы определить, ожидает ли еще побочный эффект от предыдущего изменения состояния, и, следовательно, дополнительный запрос не нужен.

Я думал об объединении или пакетной обработке запросов. Удаление дубликатов должно работать нормально. Фактически, он также должен обрабатывать случай отложенных изменений состояния, так как они все равно будут возвращаться из функции реакции (и, таким образом, де-дублироваться), пока не вернется ответ сервера.

Сначала я думал, что, возможно, можно сохранить все состояние в состоянии приложения, но когда я начал думать о недавней проблеме с секундомером, я понял, что хотя тот факт, что секундомер isOn должен храниться в состоянии приложения, фактический объект интервала, связанный с этим секундомером нужно где-то хранить. isOn должен находиться в состоянии приложения, но не только в этом случае.

Я думаю, что текущие ожидающие реакции подобны вашим реагирующим компонентам. Технически у них есть какое-то внутреннее состояние, но мы моделируем их как функцию текущего состояния.

Хм ... Я думаю, что мы не можем иметь в виду одно и то же, говоря о логике предметной области.

На мой взгляд, функция response () инкапсулирует логику предметной области и отделена от функции doReactions (), которая обрабатывает логику применения реакций. Но вы, кажется, имеете в виду другое ...

Я как бы взял весь модуль /reactions/index в целом, но да, я согласен с тем, что функция reactions является чисто доменной логикой. Но вместо того, чтобы быть в доменном модуле, он заключен вместе с шаблоном doReactions . Это не для того, чтобы сбить с толку вашу методологию, это просто усложняет с первого взгляда различие между кодом библиотеки и кодом приложения.

Тогда мне кажется, что сама doReactions довольно сильно связана с конкретным методом извлечения данных из API. Я бы предположил, что расширенная библиотека реакций может быть способом регистрации обработчиков для различных типов эффектов.

Это не для того, чтобы сбить с толку ваш метод; Я считаю этот подход действительно привлекательным.

Я не уверен, что состояние реагирующего компонента - хорошая аналогия, поскольку большинство состояний реагирования
должен быть в состоянии приложения, но, по-видимому, должен быть какой-то способ
для поддержания состояния между событиями отправки, которые не могут быть помещены в
хранить.

Я думаю, что такое состояние @yelouafi называет состоянием управления и
возможно, саги - хороший способ смоделировать несериализуемое состояние
система как независимый наблюдатель / актер.

Я думаю, что меня меньше беспокоит состояние скрытых саг, если саги
реагировал только на события (реакции), генерируемые приложением, а не на пользователя
инициированные события (действия), поскольку это позволит редуктору приложения использовать
текущее состояние и любую условную бизнес-логику, чтобы определить,
приложение должно допускать желаемый побочный эффект без дублирования
бизнес-логика.
В понедельник, 4 января 2016 г., в 17:56 Уинстон Эверт [email protected]
написал:

В основном я имел в виду, что если одно событие вызывает изменение состояния, в котором
несколько компонентов запросили одну и ту же информацию, тогда они могли бы
оптимизировать их. Вы правы, однако, что этого недостаточно, чтобы
определить, сохраняется ли побочный эффект от предыдущего изменения состояния
и поэтому в дополнительном запросе нет необходимости.

Я думал об объединении или пакетной обработке запросов. Устранение дубликатов
должно работать нормально. Фактически, он должен обрабатывать случай ожидающего состояния
изменения тоже хорошо, так как они все равно будут возвращены из
функции реакции (и, таким образом, устранение дублирования) до тех пор, пока сервер не ответит
возвращается.

Сначала я думал, что можно сохранить все состояние в приложении
состояние, но когда я начал думать о недавней проблеме с секундомером, я
понял, что хотя тот факт, что секундомер isOn должен храниться в
приложение указывает фактический объект интервала, связанный с этим
секундомер нужно где-то хранить. isOn должен быть в приложении
состояние, но это не только не является достаточным состоянием в этом случае.

Как я думаю, текущие ожидающие реакции похожи на ваши
реагируют компоненты. Технически у них есть какое-то внутреннее состояние, но мы моделируем
их в зависимости от текущего состояния.

-
Ответьте на это письмо напрямую или просмотрите его на GitHub
https://github.com/rackt/redux/issues/1182#issuecomment -168858051.

Это не для того, чтобы сбить с толку вашу методологию, это просто усложняет с первого взгляда различие между кодом библиотеки и кодом приложения.

Это совершенно справедливо.

Тогда мне кажется, что сама doReactions довольно тесно связана с конкретным методом конкретного действия, извлекающего данные из API. Я бы предположил, что расширенная библиотека реакций может быть способом регистрации обработчиков для различных типов эффектов.

да. Я все еще пытаюсь придумать лучший способ разделить это. Это осложняется проблемой проверки равенства.

Я не уверен, что состояние реагирующего компонента - хорошая аналогия, поскольку большинство состояний реагирования
должен быть в состоянии приложения, но, по-видимому, должен быть какой-то способ
для поддержания состояния между событиями отправки, которые не могут быть помещены в
хранить.

Извините, я ошибся с аналогией. Моя цель - не сравнивать состояние внешнего действия с состоянием реагирования компонента, а с состоянием DOM. Интервал или XMLHttpRequest больше похож на элементы DOM, которые реагируют на создание и уничтожение. Вы просто говорите React, какой должна быть текущая DOM, и заставляете ее реализоваться. Точно так же вы просто возвращаете набор текущих внешних реакций, и фреймворк отменяет или запускает действие, чтобы сделать его истинным.

Мне этот подход тоже очень интересен. Рассматривали ли вы использование нескольких doReactions , которые принимают разные сопоставления состояний? Я думаю, это было бы похоже на cyclejs, где вы можете создавать многоразовые драйверы:

function main(action$) {
  const state$ = action$.startWith(INITIAL_STATE).scan(reducer);

  return { 
    DOM: state$.map(describeDOM),
    HTTP: state$.map(describeRequests),
    ...
  };
}

Одно отличие состоит в том, что вы не запрашиваете у драйверов события для получения потока действий ( const someEvent$ = sources.DOM.select('.class').events('click') ), а указываете действия непосредственно в приемнике ( <button onClick={() => dispatch(action())} /> ), как вы это делали для HTTP-запросов. также.

Я думаю, что аналогия с React работает очень хорошо. Я бы не стал рассматривать DOM как внутреннее состояние, а скорее как API, с которым он работает, в то время как внутреннее состояние состоит из экземпляров компонентов и виртуального dom.

Вот идея API (с использованием React; HTTP тоже можно построить так):

// usage
const describe = (state, dispatch) => <MyComponent state={state} dispatch={dispatch} />;
const driver = createReactDOMDriver({ container } /* opts */);
store.subscribe(() => driver.update(describe(store.getState(), store.dispatch)); 
// (could be simplified further to eg. `store.use(driver, describe)` )

// implementation
const createReactDOMDriver = ({ container }) => {
  return {
    update: (element) => ReactDOM.render(element, container),
    destroy: () => ReactDOM.unmountComponentAtNode(container),
  };
};

Я бы использовал describe take getState (а не снимок состояния) и dispatch . Таким образом, он может быть настолько асинхронным, насколько захочет.

Рассматривали ли вы использование нескольких doReactions, которые принимают разные сопоставления состояний?

Я вкратце подумал об этом, и сейчас я немного возвращаюсь к нему. Это делает естественным наличие разных библиотек реакций, которые делают разные вещи: одна для DOM, одна для http, одна для таймеров, одна для веб-аудио и т. Д. Каждая из них может выполнять оптимизацию / поведение, соответствующее ее собственному случаю. Но это кажется менее полезным, если у вас есть приложение, которое выполняет кучу разовых внешних действий.

Я бы хотел, чтобы описание получило getState (а не снимок состояния) и отправило. Таким образом, он может быть настолько асинхронным, насколько захочет.

Я бы не стал. На мой взгляд, мы хотим ограничить асинхронность там, где это возможно, а не предоставлять дополнительные способы ее использования. Все, что вы можете захотеть вызвать getState (), должно выполняться в функции reducer или Reaction. (Но это мое пуристическое мышление, и, возможно, есть прагматический довод в пользу того, чтобы не следовать ему.)

Честная оценка. Я не совсем продумал соответствие вашей идеи и примера @taurose . Я поспешно предположил, что describe - это функция reactions , но это может быть неправдой.

Но да, я согласен с тем, что ограничение асинхронности идеально, потому что, если я понимаю суть вашей идеи, мы хотим, чтобы продолжения были чистыми и отображали 1: 1 с конкретными аспектами в состоянии, такими как присутствие члена массива, описывающего намерение, которое данный эффект выполняется. Таким образом, на самом деле не имеет значения, выполняются ли они несколько раз, и нет никакого скрытого аспекта остановки процесса где-то в середине потока, от которого могли бы неявно зависеть другие процессы.

Я бы хотел, чтобы описание получило getState (а не снимок состояния) и отправило. Таким образом, он может быть настолько асинхронным, насколько захочет.

describe вызывается при каждом изменении состояния, поэтому я не вижу в этом необходимости. Это не значит, что он не может выполнять асинхронность. Рассмотрим компоненты реакции: вы не будете вызывать getState внутри ваших методов рендеринга или обработчиков событий, чтобы получить текущее состояние, а скорее считываете его из реквизита.

Но вы правы в том, что он не может (не должен) ничего делать асинхронно сам по себе; он должен оставить это драйверу и просто передать ему какое-то отображаемое состояние и / или обратные вызовы.

Предполагаемое описание было функцией реакции, но это может быть неправдой.

Насколько я могу судить, это почти то же самое. Одно отличие состоит в том, что reactions не получает dispatch . Итак, в то время как describe возвращает обратные вызовы, которые создают и отправляют действия, reactions возвращает создателей действий.

@winstonewert это длинная ветка, и у меня сейчас нет времени читать или проверять ваш код, но, возможно, @yelouafi сможет вам ответить.

Проект redux-saga возник в результате долгих обсуждений здесь

Я также использую концепцию саги более года в производственном приложении, и реализация менее выразительна, но не основана на генераторах. Вот несколько псевдопримеров концепции redux, которые я привел:

Реализация здесь далека от совершенства, но она дает представление.

@yelouafi знает о проблемах, связанных с использованием генераторов, которые скрывают состояние вне redux, и о том, что сложно запустить сагу на бэкэнде и передать это скрытое состояние во внешний интерфейс для универсальных приложений (если это действительно необходимо?)

Редукс-сага - это редукция-преобразователь, как Free для монады ввода-вывода. Эффекты являются декларативными и не выполняются прямо сейчас, могут быть проанализированы и запускаются в интерпретаторе (который вы можете настроить в будущем).

Я понимаю вашу точку зрения о скрытом состоянии внутри генераторов. Но действительно ли хранилище Redux - настоящий источник истины для приложения Redux? Я так не думаю. Redux записывает действия и воспроизводит их. Вы всегда можете воспроизвести эти действия, чтобы воссоздать магазин. Хранилище redux похоже на представление запроса CQRS журнала событий. Это не значит, что это должна быть единственная проекция журнала событий. Вы можете проецировать один и тот же журнал событий в разные представления запросов и прослушивать их в сагах, которые могут управлять их состоянием с помощью генераторов, глобальных изменяемых объектов или редукторов, независимо от технологии.

Имхо, создание концепции саги с помощью редуктора - неплохая идея концептуально, и я согласен с вами, что это обменное решение.
Лично после более чем 1 года использования саг в производстве я не помню ни одного случая использования, когда было бы полезно иметь возможность снимать состояние саги и восстанавливать его позже, поэтому я предпочитаю выразительность генераторов, даже если я потеряю это особенность.

Я надеюсь, что ничего из того, что я говорю, не прозвучало как атака на redux-saga. Я просто говорил о том, чем это отличается от подхода, который я придумал.

Я понимаю вашу точку зрения о скрытом состоянии внутри генераторов. Но действительно ли хранилище Redux - настоящий источник истины для приложения Redux? Я так не думаю. Redux записывает действия и воспроизводит их. Вы всегда можете воспроизвести эти действия, чтобы воссоздать магазин. Хранилище redux похоже на представление запроса CQRS журнала событий. Это не значит, что это должна быть единственная проекция журнала событий. Вы можете проецировать один и тот же журнал событий в разные представления запросов и прослушивать их в сагах, которые могут управлять их состоянием с помощью генераторов, глобальных изменяемых объектов или редукторов, независимо от технологии.

Я не совсем понимаю вашу точку зрения. Вы вроде как утверждаете, что сага - это проекция журнала событий? Но это не так. Если я воспроизведу действия, я не попаду в то же место в сагах, если сага зависит от асинхронных событий. Мне кажется неизбежным, что саги создают состояние, которого нет ни в хранилище состояний redux, ни в проекции журнала событий.

Насколько я могу судить, это почти то же самое. Одно отличие состоит в том, что реакции не отправляются напрямую. Итак, в то время как description возвращает обратные вызовы, которые создают и отправляют действия, реакции возвращают создателей действий.

Согласованный. В принципе, response может использовать один и тот же интерфейс, все обработчики событий будут использовать создателя действия, который будет отправлен при срабатывании события.

Чем больше я думаю об этом, я думаю, что между этим подходом и сагами может быть много синергии. Я полностью согласен с четырьмя пунктами, изложенными @winstonewert. Я думаю, что это хорошо, что реакции не могут видеть действия, инициированные пользователем, поскольку это предотвращает скрытое состояние и гарантирует, что бизнес-логику в редукторах не нужно дублировать в создателях действий или сагах. Однако я понял, что побочные эффекты часто создают несериализуемое состояние, которое не может быть сохранено в хранилище реакции, интервалы, объекты dom, HTTP-запросы и т. Д. Sagas, rxjs, baconjs и т. Д. Идеально подходят для этого внешнего несериализуемого состояния управления.

doReactions можно заменить сагой, и источником событий для саг должны быть реакции, а не действия.

Я надеюсь, что ничего из того, что я говорю, не прозвучало как атака на redux-saga

Нисколько. Я следил за обсуждением, но не хотел комментировать, не глядя более внимательно на ваш код.

На первый взгляд. Кажется, вы реагируете только на изменение состояния. Как я уже сказал, это был беглый взгляд. Но похоже, что это сделает реализацию сложных потоков даже сложнее, чем подход с вязом (где вы берете и состояние, и действие). это означает, что вам нужно будет сохранить в магазине еще больше состояний управления (где только изменения состояния приложения недостаточны для вывода соответствующих реакций)

Конечно, ничто не может сравниться с чистыми функциями. Я думаю, что редукторы отлично подходят для выражения переходов между состояниями, но становятся действительно странными, когда вы превращаете их в конечные автоматы.

это означает, что вам нужно будет сохранить в магазине еще больше состояний управления (где только изменения состояния приложения недостаточны для вывода соответствующих реакций)

Ага. Мне кажется, что это главный отличительный аспект этого подхода. Но мне интересно, можно ли сделать эту проблему прозрачной на практике, если разные типы эффектов можно обернуть в разные «драйверы»? Я представляю, как людям будет довольно легко просто выбрать нужные драйверы или написать свои собственные для новых эффектов.

Однако я понял, что побочные эффекты часто создают несериализуемое состояние, которое не может быть сохранено в хранилище реакции, интервалы, объекты dom, HTTP-запросы и т. Д. Sagas, rxjs, baconjs и т. Д. Идеально подходят для этого внешнего несериализуемого состояния управления.

Я пока не вижу, кто вы.

Я думаю, что редукторы отлично подходят для выражения переходов между состояниями, но становятся действительно странными, когда вы превращаете их в конечные автоматы.

Я согласен. Если вы вручную пишете сложный конечный автомат, у нас есть проблема. (На самом деле было бы неплохо, если бы мы могли преобразовать генератор в редуктор).

Но мне интересно, можно ли сделать эту проблему прозрачной на практике, если разные типы эффектов можно обернуть в разные «драйверы»? Я представляю, как людям будет довольно легко просто выбрать нужные драйверы или написать свои собственные для новых эффектов.

Я не уверен, о чем вы здесь думаете. Я вижу, как разные драйверы делают разные полезные вещи, но устраняют контрольное состояние?

@winstonewert нет, я ничего не воспринимаю как нападение. Я даже не успел по-настоящему взглянуть на ваш код :)

Я не совсем понимаю вашу точку зрения. Вы вроде как утверждаете, что сага - это проекция журнала событий? Но это не так. Если я воспроизведу действия, я не попаду в то же место в сагах, если сага зависит от асинхронных событий. Мне кажется неизбежным, что саги создают состояние, которого нет ни в хранилище состояний redux, ни в проекции журнала событий.

Нет, это не так, хранилище redux - это проекция, но сага - это старый добрый простой слушатель.

Сага (также называемая диспетчером процессов) не является новой концепцией, она берет свое начало в мире CQRS и в прошлом широко использовалась в серверных системах.

Сага - это не проекция журнала событий на структуру данных, это часть оркестровки, которая может прослушивать, что происходит в вашей системе, и генерировать реакции, остальное - это детали реализации. Обычно саги просматривают журнал событий (и, возможно, другие внешние вещи, такие как время ...) и могут создавать новые команды / события. Также, когда вы воспроизводите события в серверных системах, вы обычно отключаете побочные эффекты, вызываемые сагами.

Разница заключается в том, что в серверных системах сага часто является проекцией журнала событий: чтобы изменить свое состояние, он должен сам генерировать события и прослушивать их. В redux-saga в том виде, в котором она реализована в настоящее время, было бы сложнее воспроизвести журнал событий для восстановления состояния саги.

Я не уверен, о чем вы здесь думаете. Я вижу, как разные драйверы делают разные полезные вещи, но устраняют контрольное состояние?

Нет, не устранять его, а просто делать внутреннюю проблему реализации для большинства целей.

Мне кажется, что в сообществе Redux есть действительно твердое мнение о том, что сохранение состояния домена в магазине - огромная победа (иначе зачем вам вообще использовать Redux?). Несколько меньше общего мнения о том, что сохранение состояния пользовательского интерфейса является преимуществом, а не его инкапсуляция в компонентах. Затем есть идея синхронизации состояния браузера в магазине, например URL-адреса (redux-simple-router) или данных формы. Но это, по-видимому, последняя граница сохранения статуса / стадии длительного процесса в магазине.

Извините, если это не так, но я думаю, что очень общий подход с хорошим удобством использования разработчиками должен иметь следующие особенности:

  • Сделайте так, чтобы обычному пользователю действительно не нужно было заботиться о форме представления эффектов в магазине. Они должны взаимодействовать с простыми API, которые абстрагируются от этих деталей.
  • Сделайте так, чтобы эффекты можно было легко компоновать. Выполнение таких вещей, как поток управления и эффекты, зависящие от других эффектов, должно казаться естественным. Здесь, конечно, действительно проявляется абстракция генератора. Он хорошо работает с большинством потоков управления, замыкания являются заметным исключением. Но легко увидеть, насколько сложные асинхронные потоки выражаются в redux-saga или react-redux-controller.
  • Сделайте так, чтобы статус эффекта можно было легко раскрыть другим покупателям магазина при желании. Это позволит вам делать такие вещи, как представление пользователю статуса мультиэффектного процесса.
  • Может быть, это очевидно, но любая подсистема, которая инкапсулирует синхронизацию состояний, это состояние с Redux, посредством диспетчеризации действий.

По второму пункту, я думаю, должно быть что-то очень похожее на redux-saga. Это может быть довольно близко к тому, что я имел в виду, с его обертками call . Но сага должна быть в некотором смысле «быстрой перемоткой вперед», чтобы позволить вам десериализовать ее до промежуточного состояния.

Это довольно сложная задача, но с практической точки зрения я думаю, что если есть одна центральная, сериализуемая запись действия, отслеживающая состояние всего приложения на очень детальном уровне, то это было бы лучшим решением. способ использовать это. И я думаю, что там действительно могут быть большие победы. Я представляю себе гораздо более простой способ инструментовать приложения с помощью пользовательской аналитики и анализа производительности. Я представляю себе действительно потрясающую тестируемость, когда разные подсистемы связаны только через состояние.

Возможно, я сейчас сильно сбился с курса, так что я оставлю все как есть :)

@acjay Я думаю, что мы согласны с вами в этом вопросе, проблема в том, чтобы найти эту реализацию, которая все решает правильно :)

Но кажется трудным иметь выразительный api с генераторами и возможность путешествовать во времени и снимать / восстанавливать состояние ... Может быть, можно было бы запоминать выполнение эффекта, чтобы мы могли легко восстанавливать состояние генераторов ...

Не уверен, но это может помешать сагам в стиле while(true) { ... } . Будет ли зацикливание просто следствием развития состояния?

@acjay @slorber

Как я объяснил в (https://github.com/yelouafi/redux-saga/issues/22#issuecomment-168872101) Путешествие во времени в одиночку (то есть без горячей перезагрузки) возможно для саг. Все, что вам нужно, чтобы довести сагу до определенной точки, - это последовательность эффектов, полученных от начала до этой точки, а также их результат (разрешить или отклонить). Затем вы просто управляете генератором с этой последовательностью

В актуальной основной ветке (еще не выпущенной в npm). Саги поддерживают мониторинг, они отправляют в магазин все полученные эффекты, а также их результат в виде действий; они также предоставляют информацию об иерархии для отслеживания графа потока управления.

Этот журнал эффектов можно использовать для воспроизведения саги до заданного момента: нет необходимости делать настоящие вызовы API, поскольку журнал уже содержит прошлые ответы.

В примерах репо есть пример монитора саги (реализованный как промежуточное ПО Redux). Он прослушивает журнал эффектов и поддерживает внутреннюю древовидную структуру (хорошо построенную лениво). Вы можете распечатать трассировку потока, отправив в магазин действие {type: 'LOG_EFFECT'}

Вот запись журнала эффектов из примера async

saga-log-async

Изменить: извините ссылка на фиксированное изображение

Интригующе! И этот образ инструментов разработчика просто потрясающий.

Это классно :)

Действительно, этот монитор саги довольно крутой.

Если подумать, мне кажется, что сага решает две проблемы. Во-первых, он обрабатывает асинхронные эффекты. Во-вторых, он обрабатывает сложные взаимодействия состояний, которые в противном случае потребовали бы неприятного рукописного конечного автомата в редукторе.

Мой подход решает только первую проблему. Я не нашел необходимости во втором выпуске. Вероятно, я еще не написал достаточно кода redux, чтобы столкнуться с ним.

Да, но мне интересно, есть ли способ объединить две идеи. Обертка call redux-saga - это довольно простой уровень косвенного обращения к эффекту, но, предположив, что вы можете инициализировать промежуточное ПО с драйверами для различных типов эффектов, вы можете представить их эти эффекты как данные JSONable, отделенные от функции это на самом деле называется. Драйвер будет обрабатывать детали отправки основных изменений состояния в хранилище.

Это могло бы стать большим количеством дополнительных сложностей с небольшой практической пользой. Но просто пытаюсь проследить эту линию мышления до конца.

Хорошо, я собрал больше библиотеки и портировал реальный пример, чтобы использовать его:

Во-первых, у нас есть реализация реакций:
https://github.com/winstonewert/redux-reactions/blob/master/src/index.js
Интерфейс состоит из трех функций: startReactions принимает хранилище, функция реакции и отображение имен на драйверы. fromEmitter и fromPromiseFactory создают драйверы.

Здесь в примере вызывается startReactions для включения системы:
https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/store/configureStore.dev.js#L28

Вот основная конфигурация реакций:
https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/index.js.
Функция реакции на самом деле просто выполняет итерацию по компонентам, которые реагируют на экземпляры маршрутизатора, ища те, у которых есть функция реакции (), чтобы выяснить фактические необходимые реакции для этой страницы.

Реализация типа реакции github api находится здесь: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/api.js. В основном это копирование / вставка из промежуточного программного обеспечения, использованного в исходном примере. Критическая точка здесь: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/api.js#L79 , где он использует fromPromiseFactory для создания драйвера из функции, которая возвращает обещания.

См. Функцию реакций для конкретных компонентов здесь: https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/containers/RepoPage.js#L80.

Создатели реакции и общая логика находятся в https://github.com/winstonewert/redux-reactions/blob/master/examples/real-world/reactions/data.js

Привет народ! Raise только что опубликовал улучшитель магазина, который позволяет вам также использовать систему эффектов, подобную архитектуре Elm! Я надеюсь, что мы сможем изучить и улучшить все эти подходы в будущем, чтобы удовлетворить все потребности сообщества: smile:

https://github.com/raisemarketplace/redux-loop

Любой, кто заинтересован в обсуждении, может захотеть увидеть дальнейшее обсуждение моей идеи здесь: https://github.com/winstonewert/redux-reactions/issues/7

Вы также можете посмотреть ветку здесь, где я переделываю приложение счетчика, чтобы оно было более вязким, используя свой шаблон:
https://github.com/winstonewert/redux-reactions/tree/elmish/examples/counter

Я также обнаружил, что заново изобретаю используемый здесь подход: https://github.com/ccorcos/elmish

Привет, @yelouafi , не могли бы вы перепостить ссылку на идею монитора саги? Это действительно здорово! Ссылка вроде мертвая (404). Я бы хотел увидеть больше!

Соответствующее новое обсуждение: https://github.com/reactjs/redux/issues/1528

(Я считаю, что это связано. Извините, если это не то место)

Можем ли мы рассматривать все эффекты так же, как рендеринг DOM?

  1. jQuery - это драйвер DOM с императивным интерфейсом. React - это драйвер DOM с декларативным интерфейсом. Итак, вместо того, чтобы заказывать: «отключить эту кнопку», мы объявляем: «нам нужно, чтобы эта кнопка была отключена», и драйвер решает, какие манипуляции с DOM делать. Вместо того чтобы заказывать: « GET \product\123 », мы объявляем: «нам нужны эти данные», и драйвер решает, какие запросы отправлять / отменять.
  2. Мы используем компоненты React в качестве API для драйвера DOM. Давайте использовать их также для взаимодействия с другими драйверами.

    • <button ...> - мы строим наш слой View из "обычных" компонентов React.

    • <Map ...> - мы используем компоненты "обертки", чтобы превратить императивный интерфейс некоторой библиотеки в декларативный. Мы используем их так же, как «обычные» компоненты, но внутри они фактически являются драйверами.

    • <Chart ...> - это может быть любое из вышеперечисленных в зависимости от реализации. Итак, грань между «нормальными» компонентами и драйверами уже стирается.

    • <Http url={'/product/'+props.selectedProductId} onSuccess={props.PRODUCT_LOADED} /> (или "умный" <Service...> ) - мы строим наш сервисный слой из компонентов драйвера (без пользовательского интерфейса).

Уровни представления и обслуживания описываются с помощью компонентов React. И наши компоненты верхнего уровня (связанные) склеивают их вместе.
Таким образом, наши редукторы остаются чистыми, и мы не вводим никаких новых средств для обработки эффектов.

Не уверен, как сюда подходят new Date или Math.random .

Всегда ли возможно преобразовать императивный API в декларативный?
Как вы думаете, это вообще жизнеспособная точка зрения?

Спасибо

Учитывая, что у нас есть саги и другие замечательные инструменты для асинхронных действий, я думаю, что мы можем спокойно закрыть это сейчас. Посмотрите # 1528, чтобы узнать о некоторых интересных новых направлениях (помимо асинхронности).

Была ли эта страница полезной?
0 / 5 - 0 рейтинги