正确使用 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
因此,如果我在 reducerA
和 reducerB
上调用 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.todos
和 state.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 扩展包