Ответ 1
TL; DR
Без
combineReducers()
или аналогичного ручного кода,initialState
всегда выигрывает надstate = ...
в редукторе, потому чтоstate
, переданный редуктору,initialState
и неundefined
, поэтому синтаксис аргумента ES6 doesn Применяем в этом случае.С
combineReducers()
поведение более тонкое. Эти редукторы, состояние которых указано вinitialState
, получат этоstate
. Другие редукторы получатundefined
и из-за этого вернутся к аргументуstate = ...
по умолчанию, который они задают.В общем,
initialState
выигрывает по состоянию, указанному редуктором. Это позволяет редукторам определять исходные данные, которые имеют для них смысл как аргументы по умолчанию, но также позволяет загружать существующие данные (полностью или частично), когда вы увлажняете хранилище из некоторого постоянного хранилища или сервера.
Сначала рассмотрим случай, когда у вас есть единственный редуктор.
Предположим, вы не используете combineReducers()
.
Тогда ваш редуктор может выглядеть так:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
Теперь скажем, что вы создаете с ним магазин.
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0
Начальное состояние равно нулю. Зачем? Потому что второй аргумент createStore
был undefined
. Это state
, переданный вашему редуктору в первый раз. Когда Redux инициализирует, он отправляет действие "dummy" для заполнения состояния. Поэтому ваш редуктор counter
был вызван с state
равным undefined
. Это именно тот случай, когда "активирует" аргумент по умолчанию. Поэтому state
теперь 0
в соответствии со значением по умолчанию state
(state = 0
). Это состояние (0
) будет возвращено.
Рассмотрим другой сценарий:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42
Почему это 42
, а не 0
, на этот раз? Потому что createStore
был вызван с 42
в качестве второго аргумента. Этот аргумент превращается в state
, переданный вашему редуктору вместе с фиктивным действием. На этот раз state
не undefined (it 42
!), поэтому синтаксис аргумента по умолчанию ES6 не имеет эффекта. state
есть 42
, а 42
возвращается от редуктора.
Теперь рассмотрим случай, когда вы используете combineReducers()
.
У вас есть два редуктора:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
Редуктор, сгенерированный combineReducers({ a, b })
, выглядит следующим образом:
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
Если мы будем называть createStore
без initialState
, он будет инициализировать state
до {}
. Поэтому state.a
и state.b
будет undefined
к тому времени, когда он называет a
и b
редукторы. Как редукторы a
, так и b
получат undefined
в качестве аргументов state
, и если они укажут значения по умолчанию state
, они будут возвращены.. Как возвращается комбинированный редуктор a { a: 'lol', b: 'wat' }
объект состояния при первом вызове.
import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }
Рассмотрим другой сценарий:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }
Теперь я указал initialState
как аргумент createStore()
. Состояние, возвращенное из комбинированного редуктора, объединяет начальное состояние I, указанное для редуктора a
, с аргументом 'wat'
по умолчанию, указанным, что редуктор b
выбрал сам.
Вспомните, что делает комбинированный редуктор:
// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
return {
a: a(state.a, action),
b: b(state.b, action)
};
}
В этом случае state
был указан так, что он не вернулся к {}
. Это был объект с полем a
, равным 'horse'
, но без поля b
. Вот почему редуктор a
получил 'horse'
как его state
и с радостью вернул его, но редуктор b
получил undefined
как его state
и, таким образом, возвратил его идею по умолчанию state
(в наш пример, 'wat'
). Вот как мы получаем { a: 'horse', b: 'wat' }
взамен.
Чтобы подвести итог, если вы придерживаетесь соглашений Redux и возвращаете исходное состояние из редукторов, когда они вызываются с undefined
в качестве аргумента state
(самый простой способ реализовать это - указать state
ES6 значение аргумента по умолчанию), у вас будет хорошее полезное поведение для комбинированных редукторов. Они предпочтут соответствующее значение в объекте initialState
, которое вы передаете функции createStore()
, но если вы его не передали или если соответствующее поле не установлено, аргумент state
по умолчанию, указанный в вместо этого выбирается редуктор. Этот подход хорошо работает, поскольку он обеспечивает как инициализацию, так и гидратацию существующих данных, но позволяет отдельным редукторам reset их состояние, если их данные не были сохранены. Конечно, вы можете применить этот шаблон рекурсивно, так как вы можете использовать combineReducers()
на многих уровнях или даже составлять редукторы вручную, вызывая редукторы и предоставляя им соответствующую часть дерева состояний.