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


85

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

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

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

답변:


185

TL; DR

combineReducers()수동 코드 없이 또는 유사한 수동 코드 는 감속기에 전달 된 것이 이고 그렇지 않기 때문에 initialState항상 state = ...감속기에서 승리 하므로이 경우 ES6 인수 구문이 적용되지 않습니다.state initialState undefined

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)
  };
}

createStore없이 호출하면 initialState로 초기화 state됩니다 {}. 따라서, state.a그리고 state.b될 것입니다 undefined그것을 호출하는 시간 ab감속기. 모두 ab감속기 받게됩니다 undefined자신의 state 주장, 그들은 기본 지정하면 state값을, 그 반환됩니다. 이것이 결합 된 감속기가 { 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(). 결합 된 감속기에서 반환 된 상태는 감속기에 대해 지정한 초기 상태 를 감속기에 지정된 기본 인수 와 결합 합니다 .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인수 로 호출 될 때 감속기에서 초기 상태를 반환하면 (이를 구현하는 가장 쉬운 방법은 stateES6 기본 인수 값 을 지정하는 것입니다), 결합 된 감속기에 대한 유용한 동작입니다. 그들은 함수에 initialState전달 하는 객체 의 해당 값을 선호 createStore()하지만 전달하지 않았거나 해당 필드가 설정되지 않은 state경우 감속기에 지정된 기본 인수가 대신 선택됩니다.이 접근 방식은 기존 데이터의 초기화와 수화를 모두 제공하기 때문에 잘 작동하지만 데이터가 보존되지 않은 경우 개별 감속기가 상태를 재설정 할 수 있습니다. 물론 combineReducers()여러 레벨에서 사용할 수 있으므로이 패턴을 재귀 적으로 적용 하거나 리듀서를 호출하고 상태 트리의 관련 부분을 제공하여 리듀서를 수동으로 구성 할 수도 있습니다 .


3
대단한 세부 사항에 감사드립니다-정확히 내가 찾던 것. 여기서 API의 유연성은 훌륭합니다. 내가 보지 못한 부분은 "자식"감속기가에서 사용 된 키를 기반으로 초기 상태의 어떤 부분이 속하는지 이해한다는 것입니다 combineReducers. 다시 한번 감사드립니다.
cantera

이 답변에 감사드립니다, 댄. 답을 제대로 소화하고 있다면 감속기의 동작 유형을 통해 초기 상태를 설정하는 것이 가장 좋다는 말씀입니까? 예를 들어 GET_INITIAL_STATE를 입력하고 componentDidMount 콜백에서 해당 작업 유형에 대한 디스패치를 ​​호출합니까?
Con Antonakos 2016

@ConAntonakos 아니, 나는 이것을 의미하지 않습니다. 난 당신이 감속기, 예를 정의하는 초기 상태를 잘 선택할 수 있다는 것을 의미 function counter(state = 0, action)또는 function visibleIds(state = [], action).
Dan Abramov

1
@DanAbramov : createstore ()의 두 번째 인수를 사용하여 초기 값 대신 redux의 마지막 저장된 상태로 SPA 페이지를 다시로드 할 때 상태를 다시 수화 할 수 있습니다. 전체 저장소를 캐시하고 다시로드 할 때 보존하고 createstore 함수에 전달하는 방법을 묻는 것 같습니다.
jasan

1
@jasan : localStorage를 사용합니다. egghead.io/lessons/…
Dan Abramov

5

간단히 말해서, 초기 상태를 감속기에 전달하는 사람은 Redux이므로 아무것도 할 필요가 없습니다.

전화를 걸면 createStore(reducer, [initialState])Redux에게 첫 번째 작업이 들어올 때 감속기에 전달되는 초기 상태가 무엇인지 알려주는 것입니다.

두 번째 옵션은 스토어를 생성 할 때 초기 상태를 통과하지 못한 경우에만 적용됩니다. 즉

function todoApp(state = initialState, action)

상태는 Redux에 의해 전달 된 상태가없는 경우에만 초기화됩니다.


1
응답 해 주셔서 감사합니다. 리듀서 구성의 경우 스토어에 초기 상태를 적용한 경우 하위 / 하위 리듀서가 소유 한 초기 상태 트리의 부분을 어떻게 알 수 있습니까? 예를 menuState들어가있는 경우 메뉴 리듀서에게 state.menu상점에서의 값을 읽고 초기 상태로 사용하도록 어떻게 지시 합니까?
cantera

감사합니다. 제 질문은 분명하지 않습니다. 편집하기 전에 다른 사람들이 응답하는지 기다릴 것입니다.
cantera

1

저장소에서 그 상태를 어떻게 읽고 감속기의 첫 번째 인수로 만들까요?

combineReducers ()가 당신을 위해 일합니다. 작성하는 첫 번째 방법은별로 도움이되지 않습니다.

const rootReducer = combineReducers({ todos, users })

그러나 다른 하나는 동등합니다.

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

0

이것이 귀하의 요청에 응답하기를 바랍니다 (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 ) {
            ....
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.