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

can*_*era 83 javascript reactjs redux

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

  • 将它作为第二个参数传递给createStore(docs link)
  • 将它作为第一个参数传递给(子)减速器(docs link)

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

Dan*_*mov 178

TL; DR

没有combineReducers()或类似的手动代码,initialState总是state = ...在reducer中胜出,因为state传递给reducer的 initialState不是 undefined,所以在这种情况下不会应用ES6参数语法.

随着combineReducers()行为更加细致入微.指定状态的那些减速器initialState将接收到state.其他减速器将接收undefined ,因此将回退到state = ...它们指定的默认参数.

通常,initialState胜过reducer指定的状态.这使得Reducer可以指定对它们有意义的初始数据作为默认参数,但是当您从某个持久存储或服务器中保存商店时,还允许加载现有数据(完全或部分).

首先让我们考虑一下你有一个减速器的情况.
说你不用combineReducers().

然后你的reducer可能看起来像这样:

function counter(state = 0, action) {
  switch (action.type) {
  case 'INCREMENT': return state + 1;
  case 'DECREMENT': return state - 1;
  default: return state;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在让我们说你用它创建一个商店.

import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0
Run Code Online (Sandbox Code Playgroud)

初始状态为零.为什么?因为第二个论点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
Run Code Online (Sandbox Code Playgroud)

这次是为什么42而不是0?因为createStore被称为42第二个参数.这个参数state与虚拟动作一起传递给你的reducer.这一次,state未定义(它是42!),因此ES6默认参数语法无效.state42,和42从减速机返回.


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

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

function b(state = 'wat', action) {
  return state;
}
Run Code Online (Sandbox Code Playgroud)

生成的reducer combineReducers({ a, b })看起来像这样:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}
Run Code Online (Sandbox Code Playgroud)

如果我们在createStore没有的情况下打电话initialState,那么它将初始化state{}.因此,state.astate.bundefined通过它调用的时间ab减速.双方ab减速会收到undefined作为他们的 state论点,如果他们指定默认state值,这些将被退回.这是组合的reducer { a: 'lol', b: 'wat' }在第一次调用时返回状态对象的方式.

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

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

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

现在我指定了initialState作为参数createStore().从组合的reducer返回的状态我为areducer 指定的初始状态与reducer选择自己'wat'指定的默认参数组合在一起b.

让我们回想一下联合减速机的作用:

// const combined = combineReducers({ a, b })
function combined(state = {}, action) {
  return {
    a: a(state.a, action),
    b: b(state.b, action)
  };
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,state被指定,所以它没有回归{}.这是一个a场地等于'horse'但没有b场地的物体.这就是为什么a减速器收到'horse'了它state并且很高兴地返回它,但是b减速器收到undefinedstate并因此返回的默认想法state(在我们的例子中'wat').这就是我们获得{ a: 'horse', b: 'wat' }回报的方式.


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

  • 感谢非常详细 - 正是我所寻找的.您的API的灵活性非常棒.我没有看到的部分是"child"减速器将根据`combineReducers`中使用的键来理解初始状态的哪一部分属于它.非常感谢你. (3认同)

Emi*_*uez 5

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

当你打电话时,createStore(reducer, [initialState])你让Redux知道当第一个动作进入时,传递给减速器的初始状态是什么.

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

function todoApp(state = initialState, action)

只有在Redux没有通过状态时才会初始化状态