Created
August 9, 2017 20:31
-
-
Save vincent-prz/82ad6bd8dbf88b16df9af71d98b0b4db to your computer and use it in GitHub Desktop.
Combine Redux reducers like a Tetris ninja
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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