TL; DR
Без combineReducers()
или аналогичного ручного кода initialState
всегда побеждает state = ...
в редукторе, потому что state
переданный редуктору есть, initialState
а не нет undefined
, поэтому синтаксис аргумента ES6 в этом случае не применяется.
С 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());
Начальное состояние равно нулю. Почему? Потому что второй аргумент createStore
был undefined
. Это state
передается вашему редуктору в первый раз. Когда Redux инициализируется, он отправляет «фиктивное» действие для заполнения состояния. Итак, ваш counter
редуктор назывался state
равным undefined
. Именно в этом случае «активируется» аргумент по умолчанию. Следовательно, state
теперь значение 0
по умолчанию state
( state = 0
). Это состояние ( 0
) будет возвращено.
Рассмотрим другой сценарий:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState());
Почему это так 42
, а не в 0
этот раз? Потому что createStore
был назван 42
вторым аргументом. Этот аргумент state
передается вашему редуктору вместе с фиктивным действием. На этот раз state
не является неопределенным (это 42
!), Поэтому синтаксис аргумента по умолчанию ES6 не действует. state
Это 42
, и 42
возвращается из редуктора.
Теперь рассмотрим случай, когда вы используете combineReducers()
.
У вас есть два редуктора:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
Редуктор, созданный с помощью, 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: 'lol', b: 'wat' }
объект состояния при первом вызове.
import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState());
Рассмотрим другой сценарий:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState());
Теперь я указал в initialState
качестве аргумента createStore()
. Состояние, возвращаемое комбинированным редуктором, объединяет начальное состояние, которое я указал для a
редуктора, с 'wat'
аргументом по умолчанию, указанным, что 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
вместо него будет выбран аргумент по умолчанию, указанный редуктором.Этот подход работает хорошо, поскольку он обеспечивает как инициализацию, так и гидратацию существующих данных, но позволяет отдельным редукторам сбрасывать свое состояние, если их данные не были сохранены. Конечно, вы можете применять этот шаблон рекурсивно, как вы можете использовать combineReducers()
на многих уровнях, или даже составлять редукторы вручную, вызывая редукторы и передавая им соответствующую часть дерева состояний.
combineReducers
. Еще раз большое спасибо.