FAQ по экшенам¶
Почему type
должен быть строкой или по крайней мере сериализуемым? Почему мои типы экшенов должны быть константами?¶
Как и состояние, серилизуемые экшены позволяют использовать несколько определяющих Redux особенностей, таких как отладка с помощью путешествия во времени (time travel) и запись и воспроизведение экшенов. Использование чего-то вроде Symbol
как типа значений или instanceof
для проверки типа экшенов сломает это. Строки — сериализуемы и хорошо себя документируют, поэтому они являются наилучшим типом для экшенов. Важно помнить, что можно использовать типы Symbols, Promises, или другие несериализуемые значения в экшенах, если экшены предназначены для мидлваров. Экшены должны быть сериализуемыми только в тех случаях, когда они фактически взаимодействуют со стором и передаются редьюсерам.
Мы не можем полостью обеспечить сериализуемые экшены по причинам производительности, поэтому Redux только проверяет, что каждый экшен — это простой объект и его тип
определен. Остальное зависит от вас, но вы можете заметить, что использование сериализуемых типов помогает в отладке и воспроизведении проблем.
Инкапсуляция и централизация частоиспользуемых кусков кода является ключевым понятием в программировании. Конечно, пока возможно вручную создавать объекты экшенов где угодно и вручную писать каждый тип значения, но определение многоразовых констант делает поддержку кода легче. Если Вы вынесите константы в отдельный файл, то сможете проверять Ваш import
на опечатки, таким образом вы не сможете случайно использовать неправильные строки.
Документация
Обсуждения
- #384: Recommend that Action constants be named in the past tense
- #628: Solution for simple action creation with less boilerplate
- #1024: Proposal: Declarative reducers
- #1167: Reducer without switch
- Stack Overflow: Why do you need 'Actions' as data in Redux?
- Stack Overflow: What is the point of the constants in Redux?
Всегда ли редьюсеры и экшены преобразуются "один к одному"?¶
Нет. Мы предлагаем вам писать маленькие независимые функции-редьюсеры, которые отвечают за обновление отдельных частей состояния. Мы называем этот подход “композиция редьюсеров”. При этом экшен может быть обработано всеми, какими-то или ни одним из них. Это позволяет компонентам существовать отдельно от фактического изменения данных, так один экшен может затрагивать разные части дерева состояния, и компоненту не нужно знать об этом. Некоторые пользователи предпочитают связывать их вместе, как в принципе “ducks”, но, скорее всего, не "один к одному". Вы должны прекратить использовать эту парадигму, как только чувствуете, что хотите обрабатывать экшен в нескольких редьюсерах.
Документация
Обсуждения
- Twitter: most common Redux misconception
- #1167: Reducer without switch
- Reduxible #8: Reducers and action creators aren't a one-to-one mapping
- Stack Overflow: Can I dispatch multiple actions without Redux Thunk middleware?
Как я могу выполнять "побочные эффекты", такие как AJAX вызовы? Зачем нам нужны вещи типа “генераторов экшенов, “thunks” или “мидлвар” для осуществления асинхронного поведения?¶
Это длинная и сложная тема, с большим разнообразием мнений о том, как код должен быть организован и какие подходы должны быть использованы.
Любое полноценное веб-приложение должно выполнять сложную логику, обычно включающую асинхронную работу, такую как AJAX запросы. Этот код уже не является чистой функцией. Такое взаимодействие с окружающим миром носит название “побочные эффекты”.
Redux вдохновлен функциональным программированием и из коробки выполнение побочных эффектов в нем не имеет места. В частности, функции редьюсера всегда должны быть чистыми функциями типа (state, action) => newState
. Однако, мидлвары Redux-а позволяют перехватывать экшены и добавлять к ним сложное поведение, включая побочные эффекты.
В целом, Redux предполагает, что код с побочными эффектами должен быть частью процесса создания экшенов. Пока эта идея может быть реализуема внутри UI компонента. В большинстве случаев имеет смысл вынести эту логику в переиспользуемую функцию, тогда она может быть вызвана из нескольких мест — иначе говоря, получаем функцию-генератор экшена (action creator).
Самый простой и часто используемый путь сделать это — добавить Redux Thunk мидлвар, который позволяет Вам писать генераторы экшенов с более сложной и асинхронной логикой.
Другой широко применяющийся подход — Redux Saga, который позволяет Вам писать выглядящий более синхронизированным код, используя генераторы и может действовать как “фоновые потоки” или “демоны” в Redux-приложении.
Еще один подход — Redux Loop, который инвертирует процесс, позволяя вашим редьюсерам объявлять побочные эффекты в ответ на изменение состояния и выполнять их отдельно.
Кроме того, много других разработанных сообществом библиотек и идей, каждая со своим видинием того, как побочные эффекты должны быть обработаны.
Документация
- Продвинутое использование: Асинхронные экшены
- Продвинутое использование: Асинхронные потоки
- Продвинутое использование: Мидлвары
Статьи
- Redux Side-Effects and You
- Pure functionality and side effects in Redux
- From Flux to Redux: Async Actions the easy way
- React/Redux Links: "Redux Side Effects" category
- Gist: Redux-Thunk examples
Обсуждения
- #291: Trying to put API calls in the right place
- #455: Modeling side effects
- #533: Simpler introduction to async action creators
- #569: Proposal: API for explicit side effects
- #1139: An alternative side effect model based on generators and sagas
- Stack Overflow: Why do we need middleware for async flow in Redux?
- Stack Overflow: How to dispatch a Redux action with a timeout?
- Stack Overflow: Where should I put synchronous side effects linked to actions in redux?
- Stack Overflow: How to handle complex side-effects in Redux?
- Stack Overflow: How to unit test async Redux actions to mock ajax response
- Stack Overflow: How to fire AJAX calls in response to the state changes with Redux?
- Reddit: Help performing Async API calls with Redux-Promise Middleware.
- Twitter: possible comparison between sagas, loops, and other approaches
Какой асинхронный мидлвар должен я использовать? Как вы выбираете между thunks, sagas, observables или что-то еще?¶
Существует достаточно много мидлвар для обеспечения асинхронных / побочных эффектов, но наиболее часто используемые следующие redux-thunk
, redux-saga
и redux-observable
. Это разные инструменты с разными преимуществами, недостатками и вариантами использования.
В качестве общего правила:
- Thunks лучше всего подходят для сложной синхронной логики (особенно кода, который требует доступа ко всему состоянию хранилища Redux) и простой асинхронной логики (как базовые вызовы AJAX). С использованием
async/await
может быть разумно использовать thunk для некоторой более сложной логики, основанной на промисах. - Саги лучше всего подходят для сложной асинхронной логики и несвязанного поведение типа «фонового потока», особенно если вам необходимо слушать отправленные экшены (что нельзя сделать с помощью thunks). Они требуют знания ES6 генераторов и операторов «эффектов» «redux-saga».
- Observables решают те же проблемы, что и саги, но полагаются на RxJS для реализации асинхронного поведения. Они требуют знакомства с RxJS API.
Мы рекомендуем, чтобы большинство пользователей Redux начинали с thunks, а затем добавляли дополнительную библиотеку побочных эффектов, такую как sagas или observables, если их приложение действительно требует обработки для более сложной асинхронной логики.
Поскольку у саг и observables есть один и тот же вариант использования, приложение обычно использует один или другой, но не оба. Тем не менее, обратите внимание, что абсолютно нормально использовать, как thunks, так и sagas или observables вместе, потому что они решают разные проблемы.
Статьи
- Decembersoft: What is the right way to do asynchronous operations in Redux?
- Decembersoft: Redux-Thunk vs Redux-Saga
- Redux-Thunk vs Redux-Saga: an overview
- Redux-Saga V.S. Redux-Observable
Обсуждения
- Reddit: discussion of using thunks and sagas together, and pros and cons of sagas
- Stack Overflow: Pros/cons of using redux-saga with ES6 generators vs redux-thunk with ES2017 async/await
- Stack Overflow: Why use Redux-Observable over Redux-Saga?
Должен ли я отправлять несколько экшенов подряд от одного генератора экшенов?¶
Нет точного правила, как вы должны структурировать свои экшены. Использование такого асинхронного мидлвара, как Redux Thunk, конечно, позволяет выполнять такие операции, как
- отправка нескольких различных, но взаимосвязанных экшенов подряд;
- обработка экшенов, отображающих прогресс AJAX запроса;
- обработка экшенов, основанных на состоянии;
- обработка других экшенов и проверка обновленного состояния сразу же после этого.
В целом, спросите себя, эти экшены взаимосвязаны, но самостоятельны или на самом деле должны быть представлены одним экшеном? Делайте то, что имеет смысл для вашей конкретной ситуации, но старайтесь соблюдать баланс между читабельностью редьюсеров и логом экшенов. Например, для экшена, который содержит все новое дерево состояния, можно сделать ваш редьюсер однострочным, но недостаток в том, что теперь вы не можете проследить историю причин, по которым изменения произошли, поэтому отладка становится сложнее. С другой стороны, если вы генерируете ваши экшены в цикле, чтобы держать их разделенными, то, если вы захотите ввести новый тип экшена, то оно будет обрабатываться по-другому.
Старайтесь избегать отправки нескольких синхронных экшенов за раз в тех местах, где вы беспокоитесь о производительности. Есть несколько дополнений и подходов, которые также могут дозировать отправку.
Документация
Статьи
Обсуждения
- #597: Valid to dispatch multiple actions from an event handler?
- #959: Multiple actions one dispatch?
- Stack Overflow: Should I use one or several action types to represent this async action?
- Stack Overflow: Do events and actions have a 1:1 relationship in Redux?
- Stack Overflow: Should actions be handled by reducers to related actions or generated by action creators themselves?
- Twitter: "Good thread on the problems with Redux Thunk..."