Использование combineReducers
Основные понятия
Наиболее распространённая структура состояния для приложения Redux — это обычный JavaScript-объект, содержащий «слайсы» данных, относящихся к определённой области, под каждым ключом верхнего уровня. Аналогично, наиболее распространённый подход к написанию логики редьюсера для такой структуры состояния — это использование функций «слайс-редусеров», каждая с той же (state, action) сигнатурой, и каждая из них отвечает за управление всеми обновлениями этого конкретного слайса состояния. Несколько слайс-редусеров могут реагировать на одно и то же действие, независимо обновлять свой собственный слайс по мере необходимости, и обновлённые слайсы объединяются в новый объект состояния.
Поскольку этот шаблон очень распространён, Redux предоставляет combineReducers утилиту для реализации этого поведения. Это пример редусера высшего порядка, который принимает объект со слайс-редусер-функциями и возвращает новую функцию редусера.
Существует несколько важных моментов, которые следует учитывать при использовании combineReducers:
- Прежде всего,
combineReducers— это просто утилита для упрощения наиболее распространённого случая при написании редусеров Redux. Вы не обязаны использовать её в своём приложении, и она не обрабатывает все возможные сценарии. Совершенно возможно написать логику редусера без её использования, и часто требуется написать пользовательскую логику редусера для случаев, которыеcombineReducerне обрабатывает. (См. За пределамиcombineReducersдля примеров и предложений.) - Хотя сам Redux не навязывает мнения о том, как организовано ваше состояние,
combineReducersнакладывает несколько правил, чтобы помочь пользователям избежать распространённых ошибок. (См.combineReducersдля получения подробностей.) - Часто задаваемый вопрос заключается в том, «вызывает ли Redux все редусеры» при отправке действия. Поскольку на самом деле существует только одна корневая функция редусера, ответ по умолчанию — «нет, не вызывает» . Однако,
combineReducersимеет специфическое поведение, которое действительно работает таким образом. Для сборки нового дерева состоянияcombineReducersбудет вызывать каждый слайс-редусер с его текущим слайсом состояния и текущим действием, давая слайс-редусеру возможность отреагировать и обновить свой слайс состояния при необходимости. Итак, в этом смысле использованиеcombineReducersдействительно «вызывает все редусеры», или, по крайней мере, все слайс-редусеры, которые он оборачивает. - Вы можете использовать его на всех уровнях вашей структуры редусера, а не только для создания корневого редусера. Очень распространённо иметь несколько объединённых редусеров в различных местах, которые комбинируются для создания корневого редусера.
Определение формы состояния
Существует два способа определения начальной формы и содержимого состояния вашего хранилища. Во-первых, функция createStore может принимать preloadedState в качестве своего второго аргумента. Это в основном предназначено для инициализации хранилища состоянием, которое ранее сохранялось где-то ещё, например, в localStorage браузера. Другой способ — это корневой редусер, возвращающий начальное значение состояния, когда аргумент состояния undefined. Эти два подхода подробно описаны в Инициализация состояния, но есть некоторые дополнительные моменты, которые следует учитывать при использовании combineReducers.
combineReducers принимает объект со слайс-редусер-функциями и создаёт функцию, которая выводит соответствующий объект состояния с теми же ключами. Это означает, что если предопределённое состояние не предоставляется функции createStore, имена ключей в входном объекте слайс-редусеров определят имена ключей в выходном объекте состояния. Корреляция между этими именами не всегда очевидна, особенно при использовании функций ES6, таких как экспорт модулей по умолчанию и сокращения для объектов-литералов.
Вот пример того, как использование сокращений ES6 для литералов объектов с combineReducers может определить форму состояния:
// reducers.js
export default theDefaultReducer = (state = 0, action) => state;
export const firstNamedReducer = (state = 1, action) => state;
export const secondNamedReducer = (state = 2, action) => state;
// rootReducer.js
import {combineReducers, createStore} from "redux";
import theDefaultReducer, {firstNamedReducer, secondNamedReducer} from "./reducers";
// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
theDefaultReducer,
firstNamedReducer,
secondNamedReducer
});
const store = createStore(rootReducer);
console.log(store.getState());
// {theDefaultReducer : 0, firstNamedReducer : 1, secondNamedReducer : 2}
Обратите внимание, что поскольку мы использовали сокращения ES6 для определения литералов объектов, имена ключей в результирующем состоянии совпадают с именами переменных из импортов. Это может не всегда быть желаемым поведением и часто является причиной путаницы для тех, кто не так хорошо знаком с синтаксисом ES6.
Также результирующие имена немного странные. Как правило, не рекомендуется включать в имена ключей состояния слова, такие как «редусер» — ключи должны просто отражать область или тип данных, которые они хранят. Это означает, что мы должны либо явно указать имена ключей в объекте слайс-редусеров для определения ключей в выходном объекте состояния, либо тщательно переименовать переменные для импортированных слайс-редусеров, чтобы установить ключи при использовании сокращений для литералов объектов.
Более подходящий пример использования может выглядеть так:
import {combineReducers, createStore} from "redux";
// Rename the default import to whatever name we want. We can also rename a named import.
import defaultState, {firstNamedReducer, secondNamedReducer as secondState} from "./reducers";
const rootReducer = combineReducers({
defaultState, // key name same as the carefully renamed default export
firstState : firstNamedReducer, // specific key name instead of the variable name
secondState, // key name same as the carefully renamed named export
});
const reducerInitializedStore = createStore(rootReducer);
console.log(reducerInitializedStore.getState());
// {defaultState : 0, firstState : 1, secondState : 2}
Эта форма состояния лучше отражает участвующие данные, потому что мы позаботились о настройке ключей, которые передали функции combineReducers.
© 2015–2017 Dan Abramov
Licensed under the MIT License.
http://redux.js.org/docs/recipes/reducers/UsingCombineReducers.html