UNPKG

3.15 kBJavaScriptView Raw
1import isFunction from 'lodash.isfunction';
2import last from 'lodash.last';
3import verify from '../util/verify';
4
5/**
6 * Create a higher-order reducer by combining two or more reducers,
7 * logically executing each in sequence (in essence combining their
8 * functionality into one). This is useful when combining various
9 * reducer types into one logical construct.
10 *
11 * **Please Note:** Because each reducer is able to build on what has
12 * been accomplished by a prior reducer, joinReducers cumulatively
13 * passes the state parameter that was returned from any prior reducer
14 * (in the chain of reducers to execute). In essence this is an
15 * accumulative process. While this does NOT relax the immutable
16 * constraint of the reducer's state parameter, it is possible for a
17 * down-stream reducer to receive a state parameter that is a
18 * different instance from the start of the reduction process (because
19 * an up-stream reducer needed to alter it in some way).
20 *
21 * The {{book.guide.devGuide}} discusses joinReducers() in more detail
22 * (see {{book.guide.conceptJoin}}), and additional examples can
23 * be found in {{book.guide.fullExample}}.
24 *
25 * @param {...reducerFn} reducerFns two or more reducer functions to join
26 * together.
27 *
28 * @param {InitialState} [initialState] - the optional fall-back state
29 * value used during the state initialization boot-strap process.
30 *
31 * @returns {reducerFn} a newly created reducer function (described above).
32 */
33export default function joinReducers(...reducerFns) {
34
35 // define our initialState parameter (optionally, the last parameter)
36 // NOTE: We have to do this programatically because our function
37 // accepts variable number of arguments.
38 const initialState = isFunction(last(reducerFns)) ? undefined : reducerFns.pop();
39
40 // validate params
41 const check = verify.prefix('AstxReduxUtil.joinReducers() parameter violation: ');
42
43 check(reducerFns && reducerFns.length >= 2,
44 'two or more reducerFn arguments are required');
45
46 // ... each arg MUST be a function (reducerFn)
47 const badArgNum = reducerFns.reduce( (firstBadArgNum, reducerFn, indx) => {
48 return firstBadArgNum || (isFunction(reducerFn) ? 0 : indx+1);
49 }, 0);
50 check(!badArgNum,
51 `argument position number ${badArgNum} is NOT a function ... expecting two or more reducerFns to join together`);
52
53 // expose our new higher-order reducer
54 return (state=initialState, action, originalReducerState) => {
55
56 // maintain the originalReducerState as the immutable state
57 // at the time of the start of the reduction process
58 // ... in support of joinReducers()
59 // ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}}
60 if (originalReducerState === undefined) {
61 originalReducerState = state;
62 }
63
64 // execute each reducerFn in sequence
65 return reducerFns.reduce( (nextState, reducerFn) => {
66 return reducerFn(nextState, action, originalReducerState);
67 },
68 state);
69
70 };
71}