Прочитать исходное состояние хранилища в Redux Reducer

Исходное состояние в приложении Redux можно установить двумя способами:

  • передать его как второй аргумент createStore (ссылка docs)
  • передать его в качестве первого аргумента для ваших (суб) редукторов (docs link)

Если вы передаете начальное состояние в ваш магазин, как вы читаете это состояние из магазина и сделаете его первым аргументом в ваших редукторах?

Ответ 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() на многих уровнях или даже составлять редукторы вручную, вызывая редукторы и предоставляя им соответствующую часть дерева состояний.

Ответ 2

Вкратце: это Redux тот, кто передает начальное состояние редукторам, вам ничего не нужно делать.

Когда вы вызываете createStore(reducer, [initialState]), вы позволяете Redux знать, каково исходное состояние, которое должно быть передано редуктору, когда происходит первое действие.

Второй вариант, который вы упомянули, применяется только в том случае, если вы не передали начальное состояние при создании хранилища. то есть.

function todoApp(state = initialState, action)

Состояние

будет инициализировано только в том случае, если не было состояния, переданного с помощью Redux

Ответ 3

Я надеюсь, что это ответит на ваш запрос (который я понял как инициализацию редукторов при передаче intialState и возвращении этого состояния)

Так мы это делаем (предупреждение: скопировано из кода Typescript).

Суть этого теста if(!state) в функции mainReducer (factory)

function getInitialState(): MainState {

     return {
         prop1:                 'value1',
         prop1:                 'value2',
         ...        
     }
}



const reducer = combineReducers(
    {
        main:     mainReducer( getInitialState() ),
        ...
    }
)



const mainReducer = ( initialState: MainState ): Reducer => {

    return ( state: MainState, action: Action ): MainState => {

        if ( !state ) {
            return initialState
        }

        console.log( 'Main reducer action: ', action ) 

        switch ( action.type ) {
            ....
        }
    }
}

Ответ 4

как вы читаете это состояние из магазина и делаете его первым аргументом в ваших редукторах?

combReducers() выполняет эту работу за вас. Первый способ написать это не очень полезно:

const rootReducer = combineReducers({ todos, users })

Но другой, эквивалентный, более ясен:

function rootReducer(state, action) {
   todos: todos(state.todos, action),
   users: users(state.users, action)
}