Redux, combineReducers and allowing slice reduces to access other parts of the state tree

If you need a redux reducer that adds some "injectables" as the third argument to the reducers normally combined in redux's combineReducers, try this one:
/** 
 * A combineReducers replacement that adds additional arguments
 * to the reduction call to inject different values a reducer
 * might need, read-only, from other parts of the tree. Reducer
 * order calling is not specified. If no injectables are provided
 * the overall state is included under the key "_root_".
 *
 * @param {Object} reducers Reducer object. Each key with a function is included in a final reducer.
 * @param {Object} injectables Key-Functions. All functions are called with the overall state and
 *   current action. The results are attached to an object under their original keys. 
 *   Non-function values are ignored.
 * @param {string} rootName The name of the root that is attached if injectables is empty.
 */

export function combineReducers(reducers, injectables, rootName="_root_") {

    const finalReducers = {}
    for(var prop in reducers) {
        const reducer = reducers[prop]
        if(typeof reducer === "function") {
            finalReducers[prop] = reducer
        }
    }

    return function composed(state={}, action) {
        const finalState = {}
        let hasChanged = false
        for(var prop in finalReducers) {
            const reducer =  finalReducers[prop]
            const previousState = state[prop]
            const nextState = reducer(previousState, action, process(injectables, state, action, rootName))
            if(typeof nextState === "undefined") {
                throw new Error(`Reducer for key ${prop} returned undefined.`)
            }
            finalState[prop] = nextState
            hasChanged = hasChanged || nextState !== previousState
        }
        return hasChanged ? finalState : state
    }
}

function process(injectables, state, action, rootName) {
    const rval = { [rootName]: state }
    for(var prop in injectables) {
        const func = injectables[prop]
        if(typeof func === "function") {
            rval[prop] = func(state, action)
        }
    }
    return rval
}

Popular posts from this blog

React, Redux, Recompose and some simple steps to remove "some" boilerplate and improve reuse

cats/cats-effect and webservices function composition

graphql (facebook), falcor (netflix) and odata and ...