astx-redux-util
Version:
Several redux reducer composition utilities.
93 lines (81 loc) • 3.8 kB
JavaScript
import identity from 'lodash.identity';
import isFunction from 'lodash.isfunction';
import verify from '../util/verify';
/**
* Create a higher-order reducer that conditionally executes one of
* the supplied reducerFns, based on the conditionalFn() return
* directive.
*
* The {{book.guide.devGuide}} discusses conditionalReducer() in more detail
* (see {{book.guide.conceptConditional}}), and additional examples can
* be found in {{book.guide.conceptJoin}} and {{book.guide.fullExample}}.
*
* @param {conditionalReducerCB} conditionalFn - a callback function
* whose return value determines which reducerFn is executed
* ... truthy: thenReducerFn(), falsy: elseReducerFn().
*
* @param {reducerFn} thenReducerFn - the "wrapped" reducer invoked
* when conditionalFn returns truthy.
*
* @param {reducerFn} [elseReducerFn=identity] - the
* optional "wrapped" reducer invoked when conditionalFn returns
* falsy. DEFAULT: [identity function](https://lodash.com/docs#identity)
*
* @param {InitialState} [initialState] - the optional fall-back state
* value used during the state initialization boot-strap process.
*
* @returns {reducerFn} a newly created reducer function (described above).
*/
export default function conditionalReducer(conditionalFn, thenReducerFn, elseReducerFn=identity, initialState) {
// validate params
const check = verify.prefix('AstxReduxUtil.conditionalReducer() parameter violation: ');
check(conditionalFn, 'conditionalFn argument is required');
check(isFunction(conditionalFn), 'conditionalFn argument is NOT a function');
check(thenReducerFn, 'thenReducerFn argument is required');
check(isFunction(thenReducerFn), 'thenReducerFn argument is NOT a function');
check(isFunction(elseReducerFn), 'elseReducerFn argument is NOT a function');
// expose our new higher-order reducer
// NOTE: For more info on he originalReducerState parameter, refer to the Dev Guide {{book.guide.originalReducerState}}
return (state=initialState, action, originalReducerState) => {
// maintain the originalReducerState as the immutable state
// at the time of the start of the reduction process
// ... in support of joinReducers()
// ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}}
if (originalReducerState === undefined) {
originalReducerState = state;
}
// execute either thenReducerFn or elseReducerFn, based on conditionalFn
return conditionalFn(state, action, originalReducerState)
? thenReducerFn(state, action, originalReducerState)
: elseReducerFn(state, action, originalReducerState);
};
}
//***
//*** Specification: conditionalReducerCB
//***
/**
* A callback function (used in {{book.api.conditionalReducer}}) whose
* return value determines which reducerFn is executed.
*
* @callback conditionalReducerCB
*
* @param {*} state - The current immutable state that is the
* reduction target.
*
* @param {Action} action - The standard redux Action object that
* drives the reduction process.
*
* @param {*} originalReducerState - The immutable state at the time
* of the start of the reduction process.
*
* This is useful in determining whether state has changed within a
* series of reductions {{book.api.joinReducers}} ... because each
* individual reducer only has visibility of the state within it's own
* reduction process.
*
* Further information can be found in the
* {{book.guide.originalReducerState}} discussion of the {{book.guide.devGuide}}.
*
* @returns {truthy} A truthy value indicating which reducerFn is
* executed ... truthy: thenReducerFn(), falsy: elseReducerFn().
*/