correct usage of reduce-reducers
I don't understand what reduce-reducers is meant for. Should it be used in the case that I have 2 reducer functions containing the same action?
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}
}
So if I call reduceReducer
on reducerA
and reducerB
and action 'SAME_ACTION' is invoked for {field: 0}
then I would have a next state {field: 2}
?
Also it seems to me that it kind of concatenates reducers (meaning merging them under one key).
Am I right or does reduceReducer
serve a different purpose?
The difference is:
-
combineReducers
creates nested state -
reduceReducers
creates flat state
Consider following reducers. There are no action types to make things simpler:
// 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
}
}
combineReducers
Each reducer gets an independent piece of state (see also 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
All reducers share the same state
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
* }
*/
Use cases
-
combineReducers
- each reducer manage own slice of state (e.g.state.todos
andstate.logging
). This is useful when creating a root reducer. -
reduceReducers
- each reducer manage the same state. This is useful when chaining several reducers which are supposed to operate over the same state (this might happen for example when combining several reducer created usinghandleAction
from redux-actions)
The difference is obvious from the final state shape.