Должны ли хранилища потоков или действия (или оба) касаться внешних служб? - программирование

Должны ли хранилища потоков или действия (или оба) касаться внешних служб?

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

-OR -

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

Мне кажется, что он должен быть тем или иным (а не сочетанием обоих). Если да, то почему один предпочтительный/рекомендуемый по сравнению с другим?

4b9b3361

Ответ 1

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

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

  • Все действия отправляются от создателей действия. Если вы обрабатываете асинхронные операции в своих магазинах и хотите, чтобы обработчики действий ваших магазинов были синхронными (и вам нужно, чтобы получить flux single-dispatch), ваши магазины должны будут активировать дополнительные действия SUCCESS и FAIL в ответ на асинхронную обработку. Вместо этого эти рассылки в создателях действий помогают разделить задания создателей действия и магазинов; кроме того, вам не нужно прокладывать путь через логику магазина, чтобы выяснить, куда отправляются действия. Типичное асинхронное действие в этом случае может выглядеть примерно так (измените синтаксис вызовов dispatch на основе вкуса потока, который вы используете):

    someActionCreator: function(userId) {
      // Dispatch an action now so that stores that want
      // to optimistically update their state can do so.
      dispatch("SOME_ACTION", {userId: userId});
    
      // This example uses promises, but you can use Node-style
      // callbacks or whatever you want for error handling.
      SomeDataAccessLayer.doSomething(userId)
      .then(function(newData) {
        // Stores that optimistically updated may not do anything
        // with a "SUCCESS" action, but you might e.g. stop showing
        // a loading indicator, etc.
        dispatch("SOME_ACTION_SUCCESS", {userId: userId, newData: newData});
      }, function(error) {
        // Stores can roll back by watching for the error case.
        dispatch("SOME_ACTION_FAIL", {userId: userId, error: error});
      });
    }
    

    Логика, которая в противном случае может быть дублирована в разных действиях, должна быть извлечена в отдельный модуль; в этом примере этот модуль будет SomeDataAccessLayer, который обрабатывает фактический запрос Ajax.

  • Вам нужно меньше создателей действий. Это не так много, но приятно иметь. Как упоминалось в № 2, если в ваших магазинах имеется обработка синхронных действий (и они должны), вам нужно будет активировать дополнительные действия для обработки результатов асинхронных операций. Выполнение отправлений в создателях действий означает, что один создатель действия может отправлять все три типа действий, обрабатывая результат самого асинхронного доступа к данным.

Ответ 2

Я написал этот вопрос разработчикам в Facebook, и я получил от Билла Фишера ответ:

При ответе на взаимодействие пользователя с пользовательским интерфейсом я делаю асинхронный вызов в методах создания действия.

Но когда у вас есть тикер или какой-то другой драйвер, отличный от человека, звонок из магазина работает лучше.

Важно создать действие в обратном вызове ошибки/успеха, поэтому данные всегда возникают с действиями

Ответ 3

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

В каждой реализации Flux я видел, что Actions - это в основном строки событий, которые превращаются в объекты, например, традиционно у вас будет событие с именем "anchor: clicked", но в Flux оно будет определено как AnchorActions.Clicked. Они даже настолько "тупые", что большинство реализаций имеют отдельные объекты Dispatcher, чтобы фактически отправлять события в прослушиваемые магазины.

Лично мне нравится реализация Flux Flux, где нет отдельных объектов Dispatcher, а объекты Action сами отправляют.


edit: Facebook Flux на самом деле выбирает "создателей действий", поэтому они используют интеллектуальные действия. Они также готовят полезную нагрузку, используя магазины:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/actions/ChatMessageActionCreators.js#L27 (строки 27 и 28)

Обратный вызов при завершении затем запускает новое действие на этот раз с извлеченными данными в качестве полезной нагрузки:

https://github.com/facebook/flux/blob/19a24975462234ddc583ad740354e115c20b881d/examples/flux-chat/js/utils/ChatWebAPIUtils.js#L51

Итак, я думаю, что лучшее решение.

Ответ 4

Я предоставлю аргумент в пользу "немых" действий.

Соблюдая ответственность за сбор данных просмотра в своих действиях, вы связываете свои действия с требованиями к данным ваших просмотров.

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

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

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

Цель магазина - предоставить данные для просмотра. Название "Действие" подсказывает мне, что его цель - описать изменение моего Приложения.

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

С помощью "умных" действий вам может потребоваться изменить действие "Обновить-панель" Action, чтобы использовать новый API. Однако "Обновление панели инструментов" в абстрактном смысле не изменилось. Требования к данным ваших просмотров - это то, что изменилось.

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

Ответ 5

gaeron flux-react-router-demo имеет приятное изменение полезности для "правильного" подхода.

ActionCreator генерирует обещание от внешней службы API, а затем передает обещание и три константы действий функции dispatchAsync в прокси-сервере/расширенном диспетчере. dispatchAsync всегда будет отправлять первое действие, например. "GET_EXTERNAL_DATA", и как только обещание вернется, он отправит "GET_EXTERNAL_DATA_SUCCESS" или "GET_EXTERNAL_DATA_ERROR".

Ответ 6

Если вы хотите, чтобы в один прекрасный день была создана среда разработки, сопоставимая с тем, что вы видите в знаменитом видео Bret Victor "Изобретая по принципу" , используйте немые магазины, которые являются просто проекцией действий/событий внутри структуры данных без какого-либо побочного эффекта. Это также помогло бы, если бы ваши магазины фактически были частью одной и той же глобальной неизменяемой структуры данных, например, в Redux.

Больше объяснений здесь: fooobar.com/info/29612/...