可以将REDedrs与额外的动作结合起来吗?

时间:2016-01-13 09:19:18

标签: redux

我想要的是根减速器结合其他减速器,并听取额外的动作。我找到了文档,但我无法获得任何信息。

这是一些伪代码。

const root1 = combineReducers({
  reducer1,
  reducer2,
  reducer3,
  reducer4
});

function root2(state = initState, action) {
  switch (action.type) {
    case LOAD_DATA:
      return _.assign({}, initState, action.data);
    default:
      return state;
  }
}

merge(root1, root2);

我弄清楚的唯一方法是删除combineReducers:

function root(state = initState, action) {
  switch (action.type) {
    case LOAD_DATA:
      return _.assign({}, initState, action.data);
    case ...: return ...;
    case ...: return ...;
    case ...: return ...;
    default: return state;  
  }
}

还有其他方法可以实现吗?

2 个答案:

答案 0 :(得分:3)

,您可以将combineReducers()与多个Reducer配合使用,同时还可以执行重建整个应用程序状态的操作。不可否认,这是一个奇怪的设计决策,并且不能很好地扩展到更复杂的应用程序,但你显然有一个用例。如果你想做这样的事情,你有两个选择。

选项1:分割行动

在多个reducer函数中侦听相同的动作类型是完全有效的。这是最简单的方法,虽然它涉及更多的重复。您只需将您的操作返回的每个状态分解为适用的各个reducer函数。

例如,如果这是您的整个申请状态

{
    foo: {},
    bar: {}
}

重建整个应用程序状态的操作类型为LOAD_DATA,您可以执行此操作

function foo (state = {}, action) {
    switch (action.type) {
        case 'LOAD_DATA':
            return {...state, action.result.foo}
    }
}

function bar (state = {}, action) {
    switch (action.type) {
        case 'LOAD_DATA':
            return {...state, action.result.bar}
    }
}

const root = combineReducers({
  foo,
  bar
});

这样,状态中的foobar将始终使用来自相同操作的相应数据进行重建。

选项2:构建自定义combineReducers()

没有什么可以阻止您构建自己的combineReducers()版本。如果您从头开始构建combineReducers()函数时看{4}},您会发现现有的逻辑并不复杂。您只需要侦听特定的操作类型,并在匹配时从该操作返回整个状态。这是我通过查看this video然后将2个util函数用于该函数而构建的版本

function combineReducers(reducers) {
  var fn = (val) => typeof val === 'function';
  var finalReducers = Object.keys(reducers).reduce((result, key) => {
    if (fn(reducers[key])) {
      result[key] = reducers[key]
    }
    return result
  }, {});

  return function combination(state = {}, action) {
    if (action.type === 'LOAD_DATA') {
        return completeStateReducer(action)
    } else {
        var hasChanged = false
        var fn = (reducer, key) => {
          var previousStateForKey = state[key]
          var nextStateForKey = reducer(previousStateForKey, action)
          if (typeof nextStateForKey === 'undefined') {
            var errorMessage = getUndefinedStateErrorMessage(key, action)
            throw new Error(errorMessage)
          }
          hasChanged = hasChanged || nextStateForKey !== previousStateForKey
          return nextStateForKey
        }
        var finalState = Object.keys(finalReducers).reduce((result, key) => {
            result[key] = fn(finalReducers[key], key)
            return result
          }, {})

        return hasChanged ? finalState : state
    }
  }
}

function completeStateReducer(action) {
    return action.result;
}

在重新合并这些util函数之外,我真正添加的唯一内容是关于监听LOAD_DATA操作类型然后在发生这种情况时调用completeStateReducer()而不是组合其他reducer函数。当然,这假设您的LOAD_DATA动作实际上会返回您的整个状态,但即使它没有,这也应该指向您构建自己的解决方案的正确方向。

答案 1 :(得分:2)

首先,combineReducers仅仅是一个实用函数,它简化了“此reducer函数应处理对此数据子集的更新”的常见用例。这不是必需的。

其次,这看起来几乎是https://github.com/acdlite/reduce-reducers的确切用例。这里有一个例子:https://github.com/reactjs/redux/issues/749#issuecomment-164327121

export default reduceReducers(
  combineReducers({
    router: routerReducer,
    customers,
    stats,
    dates,
    filters,
    ui
  }),
  // cross-cutting concerns because here `state` is the whole state tree
  (state, action) => {
    switch (action.type) {
      case 'SOME_ACTION':
        const customers = state.customers;
        const filters = state.filters;
        // ... do stuff
    }
  }
);

另外,我举例说明了在Redux文档的“结构化减速机”部分中使用reduceReducershttp://redux.js.org/docs/recipes/reducers/BeyondCombineReducers.html