Javascript 在 Redux Reducer 中读取 Store 的初始状态
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33749759/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Read Store's Initial State in Redux Reducer
提问by cantera
Initial state in a Redux app can be set in two ways:
Redux 应用程序中的初始状态可以通过两种方式设置:
- pass it as the second argument to
createStore
(docs link) - pass it as the first argument to your (sub-)reducers (docs link)
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?
如果您将初始状态传递给您的商店,您如何从商店中读取该状态并将其作为您的减速器中的第一个参数?
回答by Dan Abramov
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.With
combineReducers()
the behavior is more nuanced. Those reducers whose state is specified ininitialState
will receive thatstate
. Other reducers will receiveundefined
and because of thatwill fall back to thestate = ...
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 themas default arguments, but also allows loading existing data (fully or partially) when you're hydrating the store from some persistent storage or the server.
TL; 博士
没有
combineReducers()
或类似的手动代码,initialState
总是state = ...
在减速器中获胜,因为state
传递给减速器的是initialState
和不是undefined
,所以在这种情况下 ES6 参数语法不会被应用。与
combineReducers()
行为更微妙。状态在 中指定的那些减速器initialState
将收到那个state
。其他减速器将接收undefined
,因此将回退到state = ...
它们指定的默认参数。一般情况下,
initialState
胜过减速器指定的状态。这让化简器可以指定对它们有意义的初始数据作为默认参数,而且还允许在您从某些持久存储或服务器对存储进行水合时(全部或部分)加载现有数据。
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:
那么您的减速器可能如下所示:
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
. This is the state
passed to your reducer the first time. When Redux initializes it dispatches a “dummy” action to fill the state. So your counter
reducer was called with state
equal to undefined
. This is exactly the case that “activates” the default argument.Therefore, state
is now 0
as per the default state
value (state = 0
). This state (0
) will be returned.
初始状态为零。为什么?因为第二个参数createStore
是undefined
. 这是state
第一次传递给您的减速器。当 Redux 初始化时,它会调度一个“虚拟”动作来填充状态。所以你的counter
减速器被调用state
等于undefined
. 这正是“激活”默认参数的情况。因此,state
现在0
按照默认state
值 ( state = 0
)。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? Because createStore
was called with 42
as the second argument. This argument becomes the state
passed to your reducer along with the dummy action. This time, state
is not undefined (it's 42
!), so ES6 default argument syntax has no effect.The state
is 42
, and 42
is returned from the reducer.
为什么这次是42
,而不是0
?因为createStore
被称为 with42
作为第二个参数。这个参数state
与虚拟操作一起传递给你的减速器。这一次,state
不是未定义的(它是42
!),因此 ES6 默认参数语法不起作用。的state
是42
,和42
从减速机返回。
Now let's consider a case where you use combineReducers()
.
You have two reducers:
现在让我们考虑一个使用combineReducers()
.
你有两个减速器:
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 })
看起来像这样:
// 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 theirstate
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.
如果我们在createStore
没有 的情况下调用initialState
,它将初始化state
为{}
。因此,state.a
并state.b
会undefined
通过它调用的时间a
和b
减速。双方a
和b
减速会收到undefined
作为他们的state
论点,如果他们指定默认state
值,这些将被退回。这就是组合的 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()
. The state returned from the combined reducer combinesthe initial state I specified for the a
reducer with the 'wat'
default argument specified that b
reducer chose itself.
现在我将 指定initialState
为 的参数createStore()
。从组合减速器返回的状态将我为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 {}
. 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 ideaof the default state
(in our example, 'wat'
). This is how we get { a: 'horse', b: 'wat' }
in return.
在这种情况下,state
已指定,因此它不会回退到{}
. 它是一个a
字段等于的对象'horse'
,但没有b
字段。这就是为什么a
reducer 接收'horse'
它state
并很高兴地返回它,但b
reducer 接收undefined
它state
并因此返回它的默认想法state
(在我们的例子中,'wat'
)。这就是我们获得{ 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. 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.
总而言之,如果你坚持 Redux 约定并在它们undefined
作为state
参数调用时从 reducer 返回初始状态(实现这一点的最简单方法是指定state
ES6 默认参数值),你将有组合减速器的一个很好的有用行为。他们会更喜欢initialState
您传递给createStore()
函数的对象中的相应值,但如果您没有传递任何值,或者如果未设置相应字段,state
则会选择由 reducer 指定的默认参数。这种方法很有效,因为它提供了现有数据的初始化和水化,但是如果它们的数据没有被保留,它会让各个减速器重置它们的状态。当然,您可以递归地应用此模式,因为您可以combineReducers()
在许多级别上使用,甚至可以通过调用减速器并为它们提供状态树的相关部分来手动组合减速器。
回答by Emilio Rodriguez
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 知道当第一个动作进入时要传递给减速器的初始状态是什么。
The second option you mention, applies only in case you didn't pass an initial state when creating the store. i.e.
您提到的第二个选项仅适用于您在创建商店时未传递初始状态的情况。IE
function todoApp(state = initialState, action)
function todoApp(state = initialState, action)
state will only be initialised if there was no state passed by Redux
仅当 Redux 没有传递状态时才会初始化状态
回答by Nicolas
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 :
combineReducers() 为您完成这项工作。编写它的第一种方法并不是很有帮助:
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)
}
回答by Bruno Grieder
I hope this answers your request (which I understood as initializing reducers while passing intialState and returning that state)
我希望这能回答您的请求(我理解为在传递 intialState 并返回该状态时初始化减速器)
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
它的要点是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 ) {
....
}
}
}