正确使用 reduce-reducers

Posted

技术标签:

【中文标题】正确使用 reduce-reducers【英文标题】:correct usage of 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 有不同的目的?

【问题讨论】:

【参考方案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。

以上是关于正确使用 reduce-reducers的主要内容,如果未能解决你的问题,请参考以下文章

如何正确使用 Composer 安装 Laravel 扩展包

如何正确使用 Composer 安装 Laravel 扩展包

如何正确强制正确使用类方法?

如何正确使用 Composer 安装 Laravel 扩展包

C#注释的正确的使用方法有哪些?

如何正确的使用QWebEngineView