简介
Redux 中的 combineReducers
能让我们很轻易地把多个 reducers 组合起来,成为一个新的 reducer。
但是,跟着我们的运用变得愈来愈庞杂,combineReducers
有能够不能满足我们的需求。
正如 Redux 官方文档所说:
This helper is just a convenience! You can write your own combineReducers that works differently, or even assemble the state object from the child reducers manually and write a root reducing function explicitly, like you would write any other function.
combineReducers
只是轻易我们运用罢了,我们能够自定义一个完整差别的 combineReducers
来满足我们特别的需求。
道理
我们先追念一下 reducer 的写法:
const reducer = (oldState, action) => newState;
reducer 是一个一般的函数,接收两个参数:oldState
和 action
,然后返回一个 newState
。
为了把多个 reducers 组合起来,我们一般会用 Redux 自带的 combineReducers
来完成:
const rootReducer = combineReducers({
key1: key1Reducer,
key2: key2Reducer
});
先注重一下我们传了什么给 combineReducers
:
{
key1: function(state.key1, action) { /*...*/ },
key2: function(state.key2, action) { /*...*/ },
}
好了,让我们先来想想,经由 combineReducers
的处置惩罚以后,我们得到了什么呢?
不必想了,很显然我们得到了一个新的 reducer。
那这个新的 reducer 又长什么样呢?
const rootReducer = (oldState, action) => newState;
你应当不会惊奇,由于一切 reducer 都长这个模样,纵然它是已被组合过的 reducer,它也是长这个模样。
如今你应当猜到 combineReducers
做了什么了吧?实在它最基础形状是这模样的:
function combineReducers(reducers) {
return function (state, action) { /*...*/ };
}
它接收 reducers
作为参数,然后返回一个规范的 reducer 函数。
注重:
实在到了这一步,我们就可以够自定义combineReducers
了,我们完整能够写一个相似的函数,然后在里面写种种switch...case
语句来到达自定义的目标。
但我以为我们照样先看看 Redux 自带的combineReducers
做了什么比较好,由于我们自定义的combineReducers
很有能够须要本来的功用。
还记得我适才叫你注重的处所吗?没错,就是下面这个:
// reducers
{
key1: function(state.key1, action) { /*...*/ },
key2: function(state.key2, action) { /*...*/ }
}
我们来追念一下 store.dispatch(action)
的历程:当一个 action
触发的时刻,一切 reducers 都应当响应这个 action
,做出响应的转变,末了返回一个新的 store
。
对着上面这个构造,我们实在很轻易就可以写出如许的结果,还能加上一些其他的处置惩罚:
function reCombineReducers(reducers) {
return function (state, action) {
switch (action.type) {
case SP_ACTION:
return Object.assign({}, state, { /* do something */ });
default:
return Object.keys(reducers)
.map(k => ({ [k]: reducers[k](state[k], action) }))
.reduce((prev, next) => Object.assign({}, prev, next));
}
}
}
上面的例子模拟了本来 combineReducers
的功用,还对 SP_ACTION
进行了特别的处置惩罚,很简朴吧!
但是,上面的例子虽然模拟了 combineReducers 的功用,但失去了 combineReducers 的搜检对象变化的功用,由于如今的 default block 中会返回一个全新的对象。
有无要领能够既保存 combineReducers 的悉数功用,又能扩大它呢?
实在很简朴,我们只需应用 combineReducers 返回的函数就可以够了!
(谢谢 liximomo 指出上面例子中的缺点)
function reCombineReducers(reducers) {
let fn = combineReducers(reducers);
return function (state, action) {
switch (action.type) {
case SP_ACTION:
return Object.assign({}, state, { /* do something */ });
default:
return fn(state, action);
}
}
}
实例
根据 Redux 的准绳,差别的 reducer 应当互相自力的,它们之间不该当有任何依靠。
这个准绳看着是很优美的,但在实际运用中照样会有一些破例的状况。
一个很简朴的例子,也是我遇到过的例子,就是完成一个简朴的表格 (实在我的状况庞杂的多,须要完成相似 Excel 那样的操纵,同时支撑其他分外的功用)。
我们先来设想一下 state
:
// state
{
rows: { ... },
cells: { ... },
data: { ... }
}
rows
, cells
, data
都邑响应一些特定的 action
(如 CHANGE_ROW_PROPS
, CHANGE_CELL_PROPS
, CHANGE_DATA
),做出响应的转变,这些都是我们所希冀的。
但是,当涌现一些特别的 action (如 GET_TABLE_SUCCESS
,示意胜利从服务端猎取数据) 的时刻,灾害就涌现了:
一切的 reducer 都须要监听 GET_TABLE_SUCCESS
这个 action,这意味着假如我们有 n 个 reducer 的话,我们就须要修正 n 个文件!
当我再加上 UPDATE_TABLE_SUCCESS
,REMOVE_TABLE_SUCCESS
之类的 action
时,我要再修正 n 个文件!
这不合理啊,为何我加一个简朴的功用,须要修正这么多文件,最主要的是,这些修正都是异常相似!
这时刻,我们就须要自定义 combineReducers
来处理我们的需求拉:
function reCombineReducers(reducers) {
let fn = combineReducers(reducers);
return function (state, action) {
switch (action.type) {
case GET_TABLE_SUCCESS:
case UPDATE_TABLE_SUCCESS:
return Object.assign({}, state, action.payload.table);
case REMOVE_TABLE_SUCCESS:
return initState;
default:
return fn(state, action);
}
}
}
const table = reCombineReducers({
sections,
suites,
rows,
cells,
toys,
data,
logics
})
怎样,是否是比修正多个文件惬意许多?
(完)
出处
http://scarletsky.github.io/2…
参考资料
http://redux.js.org/docs/api/…
https://github.com/reactjs/re…