Структура базового редьюсера и форма состояния
Структура базового редьюсера
Прежде всего, важно понимать, что вся ваша программа фактически имеет только одну единственную функцию редьюсера: функцию, которую вы передали в createStore в качестве первого аргумента. Эта единственная функция редьюсера в конечном итоге должна выполнять несколько действий:
- В первый раз, когда вызывается редьюсер, значение
stateбудетundefined. Редьюсеру необходимо обработать этот случай, предоставив значение состояния по умолчанию перед обработкой поступающего действия. - Ему необходимо проанализировать предыдущее состояние и переданное действие и определить, какая работа должна быть выполнена.
- Если необходимо внести фактические изменения, он должен создать новые объекты и массивы с обновленными данными и вернуть их.
- Если изменения не требуются, он должен вернуть существующее состояние в неизменном виде.
Самый простой способ написания логики редьюсера — поместить всё в одно объявление функции, как это:
function counter(state, action) {
if (typeof state === 'undefined') {
state = 0; // If state is undefined, initialize it with a default value
}
if (action.type === 'INCREMENT') {
return state + 1;
}
else if (action.type === 'DECREMENT') {
return state - 1;
}
else {
return state; // In case an action is passed in we don't understand
}
}
Обратите внимание, что эта простая функция удовлетворяет всем основным требованиям. Она возвращает значение по умолчанию, если оно отсутствует, инициализируя хранилище; она определяет, какой тип обновления требуется, исходя из типа действия, и возвращает новые значения; и она возвращает предыдущее состояние, если работа не требуется.
Существуют некоторые простые изменения, которые можно внести в этот редьюсер. Во-первых, повторяющиеся if/else операторы быстро утомляют, поэтому очень часто используются операторы switch. Во-вторых, мы можем использовать значения параметров по умолчанию ES6 для обработки начального случая «нет существующих данных». С этими изменениями редьюсер будет выглядеть так:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT':
return state + 1;
case 'DECREMENT':
return state - 1;
default:
return state;
}
}
Это базовая структура, которую обычно использует функция редьюсера Redux.
Базовая форма состояния
Redux побуждает вас думать о своей программе с точки зрения данных, которые вам необходимо управлять. Данные в любой момент времени представляют собой «состояние» вашей программы, а структура и организация этого состояния обычно называют его «формой». Форма вашего состояния играет важную роль в том, как вы структурируете свою логику редьюсера.
Состояние Redux обычно имеет обычный Javascript-объект вверху дерева состояния. (Конечно, можно использовать другой тип данных вместо него, например, одно число, массив или специализированную структуру данных, но большинство библиотек предполагают, что значение верхнего уровня является обычным объектом). Наиболее распространенный способ организации данных в этом объекте верхнего уровня — дальнейшее деление данных на поддеревья, где каждый ключ верхнего уровня представляет собой определенную «область» или «срез» связанных данных. Например, состояние базового приложения Todo может выглядеть следующим образом:
{
visibilityFilter: 'SHOW_ALL',
todos: [
{
text: 'Consider using Redux',
completed: true,
},
{
text: 'Keep all state in a single tree',
completed: false
}
]
}
В этом примере todos и visibilityFilter являются ключами верхнего уровня в состоянии, и каждый представляет собой «срез» данных для определенного понятия.
Большинство приложений работают с несколькими типами данных, которые можно условно разделить на три категории:
- Данные области: данные, которые приложение должно отображать, использовать или изменять (например, «все задачи Todo, полученные с сервера»)
- Состояние приложения: данные, специфичные для поведения приложения (например, «Задача Todo #5 в настоящее время выбрана» или «в настоящее время происходит запрос на получение задач Todo»)
- Состояние пользовательского интерфейса: данные, представляющие текущее отображение пользовательского интерфейса (например, «Диалоговое окно EditTodo в настоящее время открыто»)
Поскольку хранилище представляет собой ядро вашей программы, вы должны определять форму своего состояния в терминах ваших данных области и состояния приложения, а не дерева вашего компонента пользовательского интерфейса. Например, форма state.leftPane.todoList.todos была бы плохой идеей, потому что понятие «задачи Todo» является центральным для всей программы, а не только для одной части пользовательского интерфейса. Срез todos должен быть вверху дерева состояния вместо этого.
Вряд ли будет 1:1 соответствие между деревом вашего пользовательского интерфейса и формой вашего состояния. Исключением может быть случай, если вы явно отслеживаете различные аспекты данных пользовательского интерфейса в своём хранилище Redux, но даже тогда форма данных пользовательского интерфейса и форма данных области, скорее всего, будут отличаться.
Форма состояния типичного приложения может примерно выглядеть следующим образом:
{
domainData1 : {},
domainData2 : {},
appState1 : {},
appState2 : {},
ui : {
uiState1 : {},
uiState2 : {},
}
}
© 2015–2017 Dan Abramov
Licensed under the MIT License.
http://redux.js.org/docs/recipes/reducers/BasicReducerStructure.html