【发布时间】:2016-02-18 09:36:19
【问题描述】:
Redux 应用中的初始状态可以通过两种方式设置:
如果您将初始状态传递给您的 store,您如何从 store 中读取该状态并将其作为 reducer 中的第一个参数?
【问题讨论】:
标签: javascript reactjs redux
Redux 应用中的初始状态可以通过两种方式设置:
如果您将初始状态传递给您的 store,您如何从 store 中读取该状态并将其作为 reducer 中的第一个参数?
【问题讨论】:
标签: javascript reactjs redux
简而言之:将初始状态传递给 reducer 的是 Redux,您无需执行任何操作。
当您调用createStore(reducer, [initialState]) 时,您是在让 Redux 知道当第一个操作进入时要传递给 reducer 的初始状态是什么。
您提到的第二个选项仅适用于您在创建商店时未传递初始状态的情况。即
function todoApp(state = initialState, action)
只有在 Redux 没有传递状态时才会初始化状态
【讨论】:
menuState,我如何告诉 menu reducer 从 store 中读取 state.menu 的值并将其用作其初始状态?
我希望这能回答您的请求(我理解为在传递 intialState 并返回该状态时初始化减速器)
这就是我们的做法(警告:从 Typescript 代码复制而来)。
它的要点是 mainReducer(factory) 函数中的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 ) {
....
}
}
}
【讨论】:
如何从存储中读取该状态并将其作为减速器中的第一个参数?
combineReducers() 为您完成这项工作。 第一种写法没什么用:
const rootReducer = combineReducers({ todos, users })
但另一个,也就是等价的更清楚:
function rootReducer(state, action) {
todos: todos(state.todos, action),
users: users(state.users, action)
}
【讨论】:
TL;DR
如果没有
combineReducers()或类似的手动代码,initialState总是在减速器中胜过state = ...,因为传递给减速器的state是initialState而不是undefined,所以 ES6 参数语法在这种情况下不适用。
combineReducers()的行为更加微妙。在initialState中指定状态的那些reducer 将收到state。其他 reducer 将收到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;
}
}
现在假设您使用它创建了一个商店。
import { createStore } from 'redux';
let store = createStore(counter);
console.log(store.getState()); // 0
初始状态为零。为什么?因为createStore 的第二个参数是undefined。这是第一次传递给减速器的state。当 Redux 初始化时,它会分派一个“虚拟”动作来填充状态。所以你的counter reducer 被调用为state 等于undefined。 这正是“激活”默认参数的情况。因此,state 现在按照默认的state 值 (state = 0) 变为 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 })生成的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.a 和 state.b 在调用 a 和 b 减速器时将是 undefined。 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' }
让我们考虑一个不同的场景:
import { createStore } from 'redux';
let store = createStore(combined, { a: 'horse' });
console.log(store.getState()); // { a: 'horse', b: 'wat' }
现在我将initialState 指定为createStore() 的参数。从组合减速器返回的状态结合我为 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 减速器收到'horse' 作为它的state 并欣然返回它,但b 减速器收到undefined 作为它的state 并因此返回它的想法默认state(在我们的示例中为'wat')。这就是我们获得{ a: 'horse', b: 'wat' } 的方式。
总而言之,如果您坚持 Redux 约定并在使用 undefined 作为 state 参数调用 reducer 时从 reducer 返回初始状态(实现这一点的最简单方法是指定 state ES6 默认参数值),您将对组合减速器有一个很好的有用行为。 他们会更喜欢你传递给createStore()函数的initialState对象中的对应值,但是如果你没有传递任何值,或者对应的字段没有设置,默认的state参数由而是选择了 reducer。 这种方法效果很好,因为它提供了现有数据的初始化和水合,但如果没有保留数据,则允许各个 reducer 重置其状态。当然,您可以递归地应用此模式,因为您可以在多个级别上使用 combineReducers(),甚至可以通过调用 reducer 并为它们提供状态树的相关部分来手动组合 reducer。
【讨论】:
combineReducers 中使用的密钥了解初始状态的哪一部分属于它。再次非常感谢您。
function counter(state = 0, action) 或 function visibleIds(state = [], action).