【问题标题】:correct usage of reduce-reducers正确使用 reduce-reducers
【发布时间】:2016-12-03 19:33:52
【问题描述】:

我不明白reduce-reducers 是什么意思。如果我有 2 个包含相同操作的 reducer 函数,是否应该使用它?

function reducerA(state, action){
   switch(action.type):
       ...
       case 'SAME_ACTION': {...state, field: state.field+1}
}

function reducerB(state, action){
   switch(action.type):
       ...
       case 'SAME_ACTION': {...state, field: state.field*2}
}

因此,如果我在 reducerAreducerB 上调用 reduceReducer 并为 {field: 0} 调用操作“SAME_ACTION”,那么我将有下一个状态 {field: 2}

在我看来,它有点连接化简器(意味着将它们合并到一个键下)。

我是对的还是reduceReducer 有不同的目的?

【问题讨论】:

    标签: redux reducers


    【解决方案1】:

    区别在于:

    • combineReducers 创建嵌套状态
    • reduceReducers 创建 flat 状态

    考虑以下减速器。没有动作类型可以让事情变得更简单:

    // this reducer adds a payload to state.sum 
    // and tracks total number of operations
    function reducerAdd(state, payload) {
      if (!state) state = { sum: 0, totalOperations: 0 }
      if (!payload) return state
    
      return {
        ...state,
        sum: state.sum + payload,
        totalOperations: state.totalOperations + 1
      }
    }
    
    // this reducer multiplies state.product by payload
    // and tracks total number of operations
    function reducerMult(state, payload) {
      if (!state) state = { product: 1, totalOperations: 0 }
      if (!payload) return state
    
      // `product` might be undefined because of 
      // small caveat in `reduceReducers`, see below
      const prev = state.product || 1
    
      return {
        ...state,
        product: prev * payload,
        totalOperations: state.totalOperations + 1
      }
    }
    

    组合减速器

    每个reducer 都有一个独立的状态(另见http://redux.js.org/docs/api/combineReducers.html):

    const rootReducer = combineReducers({
      add: reducerAdd,
      mult: reducerMult
    })
    
    const initialState = rootReducer(undefined)
    /*
     * {
     *   add:  { sum: 0, totalOperations: 0 },
     *   mult: { product: 1, totalOperations: 0 },
     * }
     */
    
    
    const first = rootReducer(initialState, 4)
    /*
     * {
     *   add:  { sum: 4, totalOperations: 1 },
     *   mult: { product: 4, totalOperations: 1 },
     * }
     */    
    // This isn't interesting, let's look at second call...
    
    const second = rootReducer(first, 4)
    /*
     * {
     *   add:  { sum: 8, totalOperations: 2 },
     *   mult: { product: 16, totalOperations: 2 },
     * }
     */
    // Now it's obvious, that both reducers get their own 
    // piece of state to work with
    

    reduceReducers

    所有reducer共享相同的状态

    const addAndMult = reduceReducers(reducerAdd, reducerMult) 
    
    const initial = addAndMult(undefined)
    /* 
     * {
     *   sum: 0,
     *   totalOperations: 0
     * }
     *
     * First, reducerAdd is called, which gives us initial state { sum: 0 }
     * Second, reducerMult is called, which doesn't have payload, so it 
     * just returns state unchanged. 
     * That's why there isn't any `product` prop.
     */ 
    
    const next = addAndMult(initial, 4)
    /* 
     * {
     *   sum: 4,
     *   product: 4,
     *   totalOperations: 2
     * }
     *
     * First, reducerAdd is called, which changes `sum` = 0 + 4 = 4
     * Second, reducerMult is called, which changes `product` = 1 * 4 = 4
     * Both reducers modify `totalOperations`
     */
    
    
    const final = addAndMult(next, 4)
    /* 
     * {
     *   sum: 8,
     *   product: 16,
     *   totalOperations: 4
     * }
     */
    

    用例

    • combineReducers - 每个 reducer 管理自己的状态切片(例如 state.todosstate.logging)。这在创建 root reducer 时很有用。
    • reduceReducers - 每个 reducer 管理相同的状态。这在链接多个应该在相同状态下运行的减速器时很有用(例如,当组合使用 redux-actions 中的 handleAction 创建的多个减速器时,可能会发生这种情况)

    从最终的状态形状来看,区别很明显。

    【讨论】:

    • 根据github.com/redux-utilities/reduce-reducers/releases的说法,初始状态问题已经解决。你能确认一下吗,@tomáš-ehrlich?
    • @Seth 很遗憾,我不能。我不从事任何仍然使用 Redux 的项目。如果已修复,您希望我从答案中删除 Caveat 段落吗?
    【解决方案2】:

    我也不明白 reduce-reducers 试图解决的问题。 @Tomáš 描述的用例可以通过一个简单的 Reducer 来实现。毕竟,Reducer 只是一个接受 app-state 和 action 的函数,并返回一个包含新 app-state 的对象。例如,您可以执行以下操作,而不是使用 redux 提供的 combineReducers:

    import combinationReducer from "./combinationReducer";
    import endOfPlayReducer from "./endOfPlayReducer";
    import feedbackReducer from "./feedbackReducer";
    
    function combineReducers(appState, action) {
      return {
        combination: combinationReducer(appState, action),
        feedbacks: feedbackReducer(appState, action),
        endOfPlay: endOfPlayReducer(appState, action)
      };
    }
    

    当然,在这里,您的 reducer 接受整个应用程序状态并仅返回它们负责的切片。同样,它只是一个功能,您可以随意自定义它。你可以阅读更多关于它here

    【讨论】:

    • 您错过了一些优化 afaik。比如我觉得React使用Object.is()或者===比较状态,每次返回一个新的状态可能会导致大量的重新渲染。
    • 您正在创建一个嵌套结构。 reduce-reducers 不是为了替换 combineReducers,而是为了让两个 reducer 在同一个状态片上运行。您可能有一个提供开箱即用的 reducer 的库,但您也有一个想要在同一状态切片上操作的自定义 reducer。你可以使用 reducerReducers 来组合这两个 reducer。
    猜你喜欢
    • 2019-03-28
    • 2017-12-22
    • 2020-05-21
    • 2017-03-08
    • 1970-01-01
    • 1970-01-01
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多