在 Redux Reducer 中读取 Store 的初始状态

Read Store's Initial State in Redux Reducer

Redux 应用中的初始状态可以通过两种方式设置:

如果您将初始状态传递给商店,您如何从商店中读取该状态并将其作为缩减器中的第一个参数?

简而言之:是 Redux 将初始状态传递给 reducer,你不需要做任何事情。

当您调用 createStore(reducer, [initialState]) 时,您是在让 Redux 知道第一个操作进入时要传递给 reducer 的初始状态是什么。

您提到的第二个选项仅适用于您在创建商店时未通过初始状态的情况。即

function todoApp(state = initialState, action)

只有在 Redux 没有传递状态时才会初始化状态

我希望这能回答您的要求(我将其理解为在传递 intialState 并返回该状态时初始化 reducer)

这就是我们的做法(警告:从 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 ) {
            ....
        }
    }
}

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

combineReducers() 为您完成这项工作。 第一种写法不是很有帮助:

const rootReducer = combineReducers({ todos, users })

但是另一个,即等价的更清楚:

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

TL;DR

Without combineReducers() or similar manual code, initialState always wins over state = ... in the reducer because the state passed to the reducer is initialState and is not undefined, so the ES6 argument syntax doesn't get applied in this case.

With combineReducers() the behavior is more nuanced. Those reducers whose state is specified in initialState will receive that state. Other reducers will receive undefined and because of that will fall back to the state = ... default argument they specify.

In general, initialState wins over the state specified by the reducer. This lets reducers specify initial data that makes sense to them as default arguments, but also allows loading existing data (fully or partially) when you're hydrating the store from some persistent storage or the server.

首先让我们考虑一个只有一个减速器的情况。
假设您不使用 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 初始化时,它会调度一个“虚拟”操作来填充状态。因此,您的 counter reducer 被调用时 state 等于 undefined这正是“激活”默认参数的情况。 因此,根据默认 state 值,state 现在是 0state = 0).此状态 (0) 将被 returned.

让我们考虑一个不同的场景:

import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42

为什么这次是42,而不是0?因为 createStore 是用 42 作为第二个参数调用的。这个参数变成 state 连同虚拟操作一起传递给你的减速器。 这一次,state不是未定义的(它是42!),所以ES6默认参数语法没有效果。 state42,并且 42 是 return 来自减速器的。


现在让我们考虑使用 combineReducers().
的情况 你有两个减速器:

function a(state = 'lol', action) {
  return state;
}

function b(state = 'wat', action) {
  return state;
}

combineReducers({ a, b })生成的reducer是这样的:

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

如果我们在没有 initialState 的情况下调用 createStore,它会将 state 初始化为 {}。因此,state.astate.b 在它调用 ab 减速器时将是 undefinedab 减速器都将接收 undefined 作为 它们的 state 参数,如果它们指定默认 state 值,这些将被 returned。 这就是组合减速器 return 在第一次调用时成为 { 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() 的参数。来自组合减速器的状态 return 结合了 我为 a 减速器指定的初始状态 'wat' 默认参数指定 b reducer 自己选择了。

让我们回忆一下组合减速器的作用:

// 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 reducer 收到 'horse' 作为它的 state 并且很高兴 return 编辑它,但是 b reducer 收到 undefined 作为它的 state,因此 return 编辑了 它的想法 默认值 state(在我们的示例中,'wat')。这就是我们如何在 return.

中得到 { a: 'horse', b: 'wat' }

总而言之,如果您坚持 Redux 约定和 return reducer 的初始状态,当它们以 undefined 作为 state 参数调用时(最简单的方法实现这个是指定 state ES6 默认参数值),你将对组合减速器有一个很好的有用行为。 他们会优先选择你传递给createStore()函数的initialState对象中的对应值,但是如果你没有传递任何东西,或者没有设置相应的字段,默认state 选择由 reducer 指定的参数。 这种方法效果很好,因为它提供了现有数据的初始化和水化,但如果数据未保留,则允许各个 reducer 重置其状态。当然,您可以递归地应用此模式,因为您可以在多个级别上使用 combineReducers(),甚至可以通过调用 reducer 并为它们提供状态树的相关部分来手动组合 reducer。