自定义 Redux 中的 combineReducers

简介

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 是一个一般的函数,接收两个参数:oldStateaction,然后返回一个 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_SUCCESSREMOVE_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…

    原文作者:scarlex
    原文地址: https://segmentfault.com/a/1190000006667043
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞