Skip to content

Instantly share code, notes, and snippets.

@vincent-prz
Created August 9, 2017 20:31
Show Gist options
  • Save vincent-prz/82ad6bd8dbf88b16df9af71d98b0b4db to your computer and use it in GitHub Desktop.
Save vincent-prz/82ad6bd8dbf88b16df9af71d98b0b4db to your computer and use it in GitHub Desktop.
Combine Redux reducers like a Tetris ninja
const applyReducer = (inputKeys, reducer, outputKeys, state, action) => {
// [NOTE] - need to handle the case where `state === undefined`.Indeed,
// Redux calls the reducers with an `undefined` state when
// the app is initializing.
const reducerArgs = inputKeys.map(key => (typeof state !== 'undefined' ? state[key] : undefined))
.concat([action])
const reduceResult = outputKeys.length === 1 ? [reducer(...reducerArgs)] : reducer(...reducerArgs)
return outputKeys.reduce(
(acc, key, index) => ({ ...acc, [key]: reduceResult[index] }), {})
}
/**
* This is for combining reducers and allow some of them to admit several
* keys. This is useful when we wish to make a reducer a function of
* sibling parts of the state.
*
* @param {Array} combinations: array of objects of the form:
* { inputKeys: {Array}, reducer: {function} outputKeys: {Array} }
* `inputKeys` is the list of fields of the state on which the reducer depends
* upon.
* `reducer` is a reducer function. Beware: a reducer admitting multiple inputKeys
* `outputKeys` specifies which keys should be given a new state. If not specified,
* it will be equal to `inputKeys`
* must have the following signature:
* Note: specified reducers must have the following signature:
* (inputState, action) -> newValueForOutputState
* or if there are multiple outputKeys
* (inputState1, inputState2, .., action) -> [newValueForOutputState1, newValueForOutputState2, ...]
*
* Important note: the ordering of combinations matters: the reducers will be
* pipelined in the same order as the combinations, and the state computed by each reducer
* will be given as input to the next reducer.
*
* @returns {Function} a reducer function which calls the reducers passed in
* `combinations` with the appriopriate inputKeys.
*
*/
const combineMultiKeyReducers = (combinations) => {
const combinedReducer = (state, action) => (
combinations.reduce((acc, { inputKeys, reducer, outputKeys }) => ({
...acc,
...applyReducer(
inputKeys,
reducer,
typeof outputKeys !== 'undefined' ? outputKeys : inputKeys,
acc,
action
) }), state
)
)
return combinedReducer
}
export default combineMultiKeyReducers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment