Действия
Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →
Redux FAQ: Действия
Почему type должен быть строкой? Зачем использовать константы для типов действий?
Как и в случае с состоянием, сериализуемые действия обеспечивают ключевые возможности Redux, такие как отладка с путешествием во времени, запись и воспроизведение действий. Использование Symbol для значения type или проверка действий через instanceof нарушили бы эту функциональность. Строки сериализуемы и легко интерпретируемы, поэтому они являются предпочтительным выбором. Примечание: допустимо использовать Symbols, Promises или другие несериализуемые значения в действиях, если они предназначены для middleware. Действия должны стать сериализуемыми только к моменту их попадания в хранилище и передачи редьюсерам.
Из соображений производительности мы не можем гарантировать сериализуемость действий, поэтому Redux проверяет лишь, что каждое действие — простой объект, а type является строкой. Остальное зависит от вас, но поддержание сериализуемости помогает в отладке и воспроизведении проблем.
Инкапсуляция и централизация часто используемого кода — фундаментальный принцип программирования. Хотя можно вручную создавать объекты действий везде и прописывать каждый type отдельно, определение переиспользуемых констант упрощает поддержку кода. Разместив константы в отдельном файле, вы сможете проверять опечатки в import, что исключит случайное использование неправильной строки.
Дополнительные материалы
Документация
Обсуждения
-
#384: Рекомендация именовать константы действий в прошедшем времени
-
#628: Решение для простого создания действий с меньшим шаблонным кодом
Всегда ли есть взаимно однозначное соответствие между редьюсерами и действиями?
Нет. Мы рекомендуем создавать небольшие независимые функции-редьюсеры, каждая из которых отвечает за обновление определённой части состояния. Этот паттерн называется «композиция редьюсеров». Одно действие может обрабатываться всеми, несколькими или ни одним из них. Это разделяет компоненты и изменения данных, поскольку одно действие может затрагивать разные части дерева состояния без ведома компонентов. Некоторые пользователи предпочитают более тесную связь, как в структуре «ducks», но изначально соответствие не является взаимно однозначным, и стоит отойти от этой парадигмы, если требуется обрабатывать действие в нескольких редьюсерах.
Дополнительные материалы
Документация
Обсуждения
-
Reduxible #8: Редьюсеры и создатели действий не имеют взаимно однозначного соответствия
-
Stack Overflow: Можно ли отправлять несколько действий без middleware Redux Thunk?
Как представить «побочные эффекты», такие как AJAX-запросы? Почему нам нужны сущности вроде «action creators», «thunks» и «middleware» для асинхронного поведения?
Это обширная и сложная тема, по которой существует множество мнений о том, как следует организовывать код и какие подходы использовать.
Любое значимое веб-приложение должно выполнять сложную логику, обычно включающую асинхронную работу, такую как выполнение AJAX-запросов. Такой код больше не является чистой функцией своих входных данных, а взаимодействия с внешним миром известны как «побочные эффекты».
Redux вдохновлён функциональным программированием и изначально не предоставляет места для выполнения побочных эффектов. В частности, функции редьюсеров должны всегда оставаться чистыми функциями вида (state, action) => newState. Однако middleware Redux позволяет перехватывать отправленные действия и добавлять дополнительное сложное поведение, включая побочные эффекты.
В целом Redux предлагает размещать код с побочными эффектами в процессе создания действий. Хотя эту логику можно выполнять внутри UI-компонента, обычно разумнее вынести её в переиспользуемую функцию, чтобы ту же логику можно было вызывать из нескольких мест — другими словами, в функцию-создатель действия.
Самый простой и распространённый способ — добавить middleware Redux Thunk, который позволяет писать создатели действий со сложной асинхронной логикой. Другой популярный метод — Redux Saga, позволяющий писать код, похожий на синхронный, с использованием генераторов, который может работать как «фоновые потоки» или «демоны» в Redux-приложении. Ещё один подход — Redux Loop, который инвертирует процесс, позволяя редьюсерам объявлять побочные эффекты в ответ на изменения состояния и выполнять их отдельно. Помимо этого, существует множество других библиотек и идей, разработанных сообществом, каждая со своим взглядом на управление побочными эффектами.
Дополнительные материалы
Документация
Статьи
Обсуждения
-
#533: Более простое введение в асинхронные создатели действий
-
#1139: Альтернативная модель побочных эффектов на основе генераторов и саг
-
Stack Overflow: Зачем нужен middleware для асинхронных операций в Redux?
-
Stack Overflow: Где размещать синхронные побочные эффекты, связанные с действиями в Redux?
-
Stack Overflow: Как обрабатывать сложные побочные эффекты в Redux?
-
Stack Overflow: Как тестировать асинхронные действия Redux с моком AJAX-ответов
-
Stack Overflow: Как выполнять AJAX-запросы при изменении состояния в Redux?
-
Reddit: Помощь в выполнении асинхронных API-вызовов с Redux-Promise Middleware
Какой асинхронный middleware выбрать? Как определиться между thunks, sagas, observables или другими решениями?
Существует множество middleware для асинхронных операций и побочных эффектов, но чаще всего используются redux-thunk, redux-saga и redux-observable. Это разные инструменты с уникальными сильными сторонами, ограничениями и сценариями применения.
Общие рекомендации:
-
Thunks лучше всего подходят для сложной синхронной логики (особенно когда нужен доступ ко всему состоянию Redux) и простых асинхронных операций (например, базовых AJAX-запросов). С использованием
async/awaitони также могут применяться для более сложной promise-логики. -
Sagas оптимальны для сложной асинхронной логики и разделения фоновых операций, особенно если нужно реагировать на отправленные действия (что невозможно с thunks). Требуют знания генераторов и операторов "effects" из
redux-saga. -
Observables решают те же задачи, что и саги, но используют RxJS для асинхронного поведения. Требуют знакомства с API RxJS.
Большинству пользователей Redux мы рекомендуем начинать с thunks, а затем добавлять sagas или observables только при реальной необходимости в обработке сложной асинхронной логики.
Поскольку саги и observables решают схожие задачи, приложение обычно использует что-то одно. Однако можно комбинировать thunks с сагами или observables, так как они решают разные проблемы.
Статьи
Обсуждения
-
Reddit: совместное использование thunks и sagas, преимущества и недостатки sagas
-
Stack Overflow: Плюсы/минусы redux-saga с генераторами ES2015 vs redux-thunk с async/await ES2017
-
Stack Overflow: Зачем использовать Redux-Observable вместо Redux-Saga?
Допустимо ли диспатчить несколько экшенов подряд из одного action creator?
Нет строгих правил структурирования экшенов. Асинхронные middleware типа Redux Thunk позволяют диспатчить несколько связанных экшенов последовательно, отражать прогресс AJAX-запроса, условно диспатчить экшены на основе состояния или даже проверить обновлённое состояние сразу после диспатча.
Оцените, являются ли экшены независимыми или должны быть объединены. Найдите баланс между читаемостью редюсеров и лога действий. Например, один экшен со всем состоянием сделает редюсер однострочным, но затруднит отладку, так как теряется история изменений. Если вы диспатчите экшены в цикле для детализации — возможно, стоит ввести новый тип экшена с особой обработкой.
Избегайте множественных синхронных диспатчей в производительно-критичных местах. Существуют решения для пакетной обработки экшенов.
Дополнительные материалы
Документация
Статьи
Обсуждения