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
. Дякую ще раз.