IT박스

Redux Reducer에서 저장소의 초기 상태 읽기

itboxs 2020. 9. 21. 07:32
반응형

Redux Reducer에서 저장소의 초기 상태 읽기


Redux 앱의 초기 상태는 두 가지 방법으로 설정할 수 있습니다.

  • 두 번째 인수로 전달 createStore( docs link )
  • (하위) 감속기에 첫 번째 인수로 전달하십시오 ( docs link )

초기 상태를 저장소에 전달하는 경우 저장소에서 해당 상태를 어떻게 읽고 감속기의 첫 번째 인수로 만들까요?


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()); // 0

초기 상태는 0입니다. 왜? 에 대한 두 번째 인수 createStoreundefined. 이것은 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

이번이 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 })다음과 같습니다.

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

If we call createStore without the initialState, it's going to initialize the state to {}. Therefore, state.a and state.b will be undefined by the time it calls a and b reducers. Both a and b reducers will receive undefined as their state arguments, and if they specify default state values, those will be returned. This is how the combined reducer returns a { a: 'lol', b: 'wat' } state object on the first invocation.

import { createStore } from 'redux';
let store = createStore(combined);
console.log(store.getState()); // { a: 'lol', b: 'wat' }

Let's consider a different scenario:

import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }

Now I specified the initialState as the argument to createStore(). The state returned from the combined reducer combines the initial state I specified for the a reducer with the 'wat' default argument specified that b reducer chose itself.

Let's recall what the combined reducer does:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}

In this case, state was specified so it didn't fall back to {}. It was an object with a field equal to 'horse', but without the b field. This is why the a reducer received 'horse' as its state and gladly returned it, but the b reducer received undefined as its state and thus returned its idea of the default state (in our example, 'wat'). This is how we get { a: 'horse', b: 'wat' } in return.


To sum this up, if you stick to Redux conventions and return the initial state from reducers when they're called with undefined as the state argument (the easiest way to implement this is to specify the state ES6 default argument value), you're going to have a nice useful behavior for combined reducers. They will prefer the corresponding value in the initialState object you pass to the createStore() function, but if you didn't pass any, or if the corresponding field is not set, the default state argument specified by the reducer is chosen instead. This approach works well because it provides both initialization and hydration of existing data, but lets individual reducers reset their state if their data was not preserved. Of course you can apply this pattern recursively, as you can use combineReducers() on many levels, or even compose reducers manually by calling reducers and giving them the relevant part of the state tree.


In a nutshell: it's Redux the one who passes the initial state to the reducers, you don't need to do anything.

When you call createStore(reducer, [initialState]) you are letting Redux know what is the initial state to be passed to the reducer when the first action comes in.

The second option you mention, applies only in case you didn't pass an initial state when creating the store. i.e.

function todoApp(state = initialState, action)

state will only be initialised if there was no state passed by Redux


how do you read that state from the store and make it the first argument in your reducers?

combineReducers() do the job for you. The first way to write it is not really helpfull :

const rootReducer = combineReducers({ todos, users })

But the other one, that is equivalent is more clear :

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

I hope this answers your request (which I understood as initializing reducers while passing intialState and returning that state)

This is how we do it (warning: copied from Typescript code).

The gist of it is the if(!state) test in the mainReducer(factory) function

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 ) {
            ....
        }
    }
}

참고URL : https://stackoverflow.com/questions/33749759/read-stores-initial-state-in-redux-reducer

반응형