Решения по архитектуре
Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →
Redux FAQ: Архитектурные решения
Почему Redux не передаёт состояние и действие подписчикам?
Подписчики должны реагировать на само значение состояния, а не на действие. Обновления состояния обрабатываются синхронно, но уведомления подписчикам могут батчиться или дебаунситься — это означает, что подписчики не всегда получают уведомления при каждом действии. Это распространённая оптимизация производительности для предотвращения повторных перерисовок.
Батчинг или дебаунсинг можно реализовать через enhancers, переопределяющие store.dispatch, чтобы изменить способ уведомления подписчиков. Также существуют библиотеки, изменяющие Redux для пакетной обработки действий:
-
redux-batch позволяет передавать массив действий в
store.dispatch()с одним уведомлением, -
redux-batched-subscribe батчит уведомления подписчикам, возникающие после диспатчей.
Гарантируется, что Redux в конечном итоге вызовет всех подписчиков с актуальным состоянием, но не то, что каждый подписчик будет вызван для каждого действия. Состояние доступно через store.getState(). Передача действия подписчикам невозможна без нарушения механизма батчинга.
Неочевидный кейс использования действия внутри подписчика (не поддерживается) — ограничение перерисовки компонента определёнными действиями. Вместо этого управляйте перерисовкой через:
-
Метод жизненного цикла shouldComponentUpdate
-
Проверку эквивалентности виртуального DOM (vDOMEq)
-
React-Redux: используйте mapStateToProps для подписки компонентов только на нужные части состояния.
Дополнительные материалы
Статьи
Обсуждения
Почему Redux не поддерживает классы для действий и редюсеров?
Использование функций (action creators) для возврата объектов действий может казаться нелогичным разработчикам с ООП-опытом, где уместны классы и экземпляры. Но экземпляры классов не поддерживаются, так как они усложняют сериализацию/десериализацию. Методы вроде JSON.parse(string) возвращают простые JS-объекты, а не экземпляры классов.
Как описано в FAQ по хранилищу, если вас устраивают ограничения в работе персистентности и time-travel отладки, вы можете хранить несериализуемые данные в Redux.
Сериализация позволяет браузеру хранить диспатченные действия и предыдущие состояния с меньшими затратами памяти. Rewinding и "горячая перезагрузка" — ключевые возможности Redux DevTools. Это также позволяет хранить десериализованные действия на сервере и повторно сериализовать их в браузере при SSR с Redux.
Дополнительные материалы
Статьи
Обсуждения
Почему в сигнатуре middleware используется каррирование?
Middleware в Redux записываются с использованием трёхуровневой структуры функций вида const middleware = storeAPI => next => action => {}, а не в виде единой функции const middleware = (storeAPI, next, action) => {}. Это обусловлено несколькими причинами.
Во-первых, "каррирование" функций — стандартный приём функционального программирования, а Redux изначально проектировался с применением его принципов. Во-вторых, каррирование создаёт замыкания, где можно объявлять переменные, существующие на протяжении всего жизненного цикла middleware (что можно считать функциональным аналогом переменных экземпляра класса). Наконец, это просто подход, выбранный при первоначальном проектировании Redux.
Каррированная сигнатура объявления middleware считается избыточной некоторыми разработчиками, поскольку и store, и next доступны при выполнении applyMiddleware. Было решено, что вносить критические изменения нецелесообразно, учитывая существование сотен middleware в экосистеме Redux, полагающихся на текущее определение.
Дополнительные материалы
Обсуждения
- Почему в сигнатуре middleware используется каррирование?
Почему applyMiddleware использует замыкание для dispatch?
applyMiddleware берёт существующий dispatch из хранилища и создаёт замыкание для формирования начальной цепочки middleware, которые вызываются с объектом, предоставляющим функции getState и dispatch. Это позволяет middleware, требующим dispatch во время инициализации, корректно работать.
Дополнительные материалы
Обсуждения
Почему combineReducers не передаёт третьим аргументом всё состояние при вызове каждого редьюсера?
combineReducers спроектирован для разделения логики редьюсеров по доменам. Как указано в разделе Помимо combineReducers, combineReducers намеренно ограничен для обработки стандартного сценария: обновление состояния в виде простого JS-объекта путём делегирования обновления каждой части состояния соответствующему редьюсеру.
Непонятно, чем должен быть потенциальный третий аргумент для каждого редьюсера: всем деревом состояния, callback-функцией, другой частью состояния и т.д. Если combineReducers не подходит для вашего случая, рассмотрите библиотеки типа combineSectionReducers или reduceReducers для работы со сложными редьюсерами, требующими доступа к глобальному состоянию.
Если ни один из опубликованных инструментов не решает вашу задачу, вы всегда можете написать собственную функцию, которая делает именно то, что вам нужно.
Дополнительные материалы
Статьи
Обсуждения
Почему mapDispatchToProps не позволяет использовать возвращаемые значения из getState() или mapStateToProps()?
Поступали запросы на использование всего state или результатов mapState внутри mapDispatch, чтобы функции, объявленные в mapDispatch, могли использовать актуальные значения из хранилища.
Этот подход не поддерживается в mapDispatch, поскольку потребовал бы вызова mapDispatch при каждом обновлении хранилища. Это привело бы к пересозданию функций при каждом изменении состояния, создавая серьёзные накладные расходы на производительность.
Предпочтительный способ решения этой задачи — изменение пропсов на основе текущего состояния и функций mapDispatchToProps — реализуется через третий аргумент connect: mergeProps. Если он указан, он получает результаты mapStateToProps(), mapDispatchToProps() и пропсы компонента. Простой объект, возвращаемый из mergeProps, будет передан как пропсы оборачиваемому компоненту.
Дополнительные материалы
Обсуждения