[英]Read Store's Initial State in Redux Reducer
Initial state in a Redux app can be set in two ways: Redux应用程序中的初始状态可以通过两种方式设置:
createStore
( docs link ) createStore
的第二个参数传递( docs链接 ) If you pass initial state to your store, how do you read that state from the store and make it the first argument in your reducers? 如果您将初始状态传递到商店,那么如何从商店中读取该状态并将其作为减速器中的第一个参数?
TL;DR
TL; DR
Without
combineReducers()
or similar manual code,initialState
always wins overstate = ...
in the reducer because thestate
passed to the reducer isinitialState
and is notundefined
, so the ES6 argument syntax doesn't get applied in this case.如果没有
combineReducers()
或类似的手动代码,initialState
总是战胜state = ...
在减速,因为state
传递到减速机是initialState
, 而不是undefined
,所以ES6参数语法不会在这种情况下得到应用。With
combineReducers()
the behavior is more nuanced.使用
combineReducers()
,行为更加微妙。 Those reducers whose state is specified ininitialState
will receive thatstate
.那些在
initialState
指定状态的reducer将接收该state
。 Other reducers will receiveundefined
and because of that will fall back to thestate = ...
default argument they specify.其他reducers将收到
undefined
,因此会回退到他们指定的state = ...
default参数。In general,
initialState
wins over the state specified by the reducer.通常,
initialState
胜过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.这使得Reducer可以指定对它们有意义的初始数据作为默认参数,但是当您从某个持久存储或服务器中保存商店时,还允许加载现有数据(完全或部分)。
First let's consider a case where you have a single reducer. 首先让我们考虑一下你有一个减速器的情况。
Say you don't use combineReducers()
. 假设您不使用
combineReducers()
。
Then your reducer might look like this: 然后你的reducer可能看起来像这样:
function counter(state = 0, action) {
switch (action.type) {
case 'INCREMENT': return state + 1;
case 'DECREMENT': return state - 1;
default: return state;
}
}
Now let's say you create a store with it. 现在让我们说你用它创建一个商店。
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0
The initial state is zero. 初始状态为零。 Why?
为什么? Because the second argument to
createStore
was undefined
. 因为
createStore
的第二个参数undefined
。 This is the state
passed to your reducer the first time. 这是
state
传递给你减速的第一次。 When Redux initializes it dispatches a “dummy” action to fill the state. 当Redux初始化时,它会调度“虚拟”动作来填充状态。 So your
counter
reducer was called with state
equal to undefined
. 所以你的
counter
减速器被调用state
等于undefined
。 This is exactly the case that “activates” the default argument. 这正是“激活”默认参数的情况。 Therefore,
state
is now 0
as per the default state
value ( state = 0
). 因此,
state
是现在0
按默认state
值( state = 0
)。 This state ( 0
) will be returned. 将返回此状态(
0
)。
Let's consider a different scenario: 让我们考虑一个不同的场景:
import { createStore } from 'redux';
let store = createStore(counter, 42);
console.log(store.getState()); // 42
Why is it 42
, and not 0
, this time? 为什么这次是
42
而不是0
? Because createStore
was called with 42
as the second argument. 因为使用
42
作为第二个参数调用createStore
。 This argument becomes the state
passed to your reducer along with the dummy action. 此参数将成为与reduction一起传递给reducer的
state
。 This time, state
is not undefined (it's 42
!), so ES6 default argument syntax has no effect. 这次,
state
未定义(它是42
!),因此ES6默认参数语法无效。 The state
is 42
, and 42
is returned from the reducer. state
为42
,从减速器返回42
。
Now let's consider a case where you use combineReducers()
. 现在让我们考虑使用
combineReducers()
。
You have two reducers: 你有两个减速器:
function a(state = 'lol', action) {
return state;
}
function b(state = 'wat', action) {
return state;
}
The reducer generated by combineReducers({ a, b })
looks like this: combineReducers({ a, b })
生成的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 {}
. 如果我们在没有
initialState
情况下调用createStore
,它将把state
初始化为{}
。 Therefore, state.a
and state.b
will be undefined
by the time it calls a
and b
reducers. 因此,
state.a
和state.b
将在调用a
和b
reducers时被undefined
。 Both a
and b
reducers will receive undefined
as their state
arguments, and if they specify default state
values, those will be returned. a
和b
reducers都将接收undefined
作为其 state
参数,如果它们指定默认state
值,则将返回这些参数。 This is how the combined reducer returns a { a: 'lol', b: 'wat' }
state object on the first invocation. 这就是组合的reducer在第一次调用时返回
{ a: 'lol', b: 'wat' }
状态对象的方式。
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()
. 现在我将
initialState
指定为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. 状态返回从组合减速器结合我为指定的初始状态
a
与减速器'wat'
默认参数指定b
减速器选择本身。
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 {}
. 在这种情况下,指定了
state
,因此它不会回退到{}
。 It was an object with a
field equal to 'horse'
, but without the b
field. 它是一个对象,
a
字段等于'horse'
,但没有b
字段。 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'
). 这就是为什么
a
减速收到'horse'
作为其state
,并愉快地回到住处,但b
收到减速undefined
其state
,从而恢复其默认的思想 state
(在本例中, 'wat'
)。 This is how we get { a: 'horse', b: 'wat' }
in return. 这就是我们获得
{ a: 'horse', b: 'wat' }
作为回报的方式。
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. 总而言之,如果你坚持使用Redux约定并在使用
undefined
调用undefined
作为state
参数时返回初始状态(实现它的最简单方法是指定state
ES6默认参数值),你就是对组合减速器有一个很好的有用行为。 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. 他们更喜欢传递给
createStore()
函数的initialState
对象中的相应值,但如果没有传递任何值,或者未设置相应的字段,则选择reducer指定的默认state
参数。 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. 这种方法很有效,因为它提供了现有数据的初始化和水合作用,但如果不保留数据,则允许各个reducers重置其状态。 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. 当然,您可以递归地应用此模式,因为您可以在许多级别上使用
combineReducers()
,甚至可以通过调用reducers并将它们赋予状态树的相关部分来手动组合combineReducers()
。
In a nutshell: it's Redux the one who passes the initial state to the reducers, you don't need to do anything. 简而言之:Redux是将初始状态传递给reducer的人,你不需要做任何事情。
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. 当你调用
createStore(reducer, [initialState])
你会让Redux知道在第一个动作进入时传递给reducer的初始状态是什么。
The second option you mention, applies only in case you didn't pass an initial state when creating the store. 您提到的第二个选项仅适用于您在创建商店时未通过初始状态的情况。 ie
即
function todoApp(state = initialState, action)
state will only be initialised if there was no state passed by Redux 只有在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. combineReducers()为您完成工作。 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) 我希望这能回答你的请求(我理解为在传递intialState并返回该状态时初始化reducers)
This is how we do it (warning: copied from Typescript code). 这就是我们这样做的方法(警告:从Typescript代码中复制)。
The gist of it is the if(!state)
test in the mainReducer(factory) function 它的要点是mainReducer(工厂)函数中的
if(!state)
测试
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 ) {
....
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.