1 | import identity from 'lodash.identity';
|
2 | import isFunction from 'lodash.isfunction';
|
3 | import verify from '../util/verify';
|
4 |
|
5 | /**
|
6 | * Create a higher-order reducer by combining a set of sub-reducer
|
7 | * functions that are indexed by the standard action.type. When no
|
8 | * action.type is acted on, the original state is merely
|
9 | * passed-through (using the [identity
|
10 | * function](https://lodash.com/docs#identity)).
|
11 | *
|
12 | * This is one of the more prevalent composition reducers, and
|
13 | * provides an alternative to the switch statement (commonly used to
|
14 | * provide this control mechanism).
|
15 | *
|
16 | * The {{book.guide.devGuide}} discusses reducerHash() in more detail (see
|
17 | * {{book.guide.conceptHash}}), and additional examples can be found in
|
18 | * {{book.guide.conceptJoin}} and {{book.guide.fullExample}}.
|
19 | *
|
20 | * **SideBar**: Because reducerHash is so central to the rudimentary
|
21 | * aspect of reduction, it is a common practice to extend it,
|
22 | * promoting a
|
23 | * [`centralized reducer-based logging capability`](/extending/logExt.md),
|
24 | * with an ability to correlate logging levels to state changes
|
25 | * *(providing a means to filter logs at a high level with minimal
|
26 | * output)*.
|
27 | *
|
28 | * @param {ActionReducerHash} actionHandlers - a hash of reducer functions,
|
29 | * indexed by the standard redux action.type.
|
30 | *
|
31 | * @param {InitialState} [initialState] - the optional fall-back state
|
32 | * value used during the state initialization boot-strap process.
|
33 | *
|
34 | * @returns {reducerFn} a newly created reducer function (described above).
|
35 | */
|
36 | export default function reducerHash(actionHandlers, initialState) {
|
37 |
|
38 | // validate params
|
39 | const check = verify.prefix('AstxReduxUtil.reducerHash() parameter violation: ');
|
40 |
|
41 | check(actionHandlers,
|
42 | 'actionHandlers is required');
|
43 |
|
44 | // ... AI: this check may be too intrusive if the client's actionHandlers object is used for OTHER things?
|
45 | const invalidHashEntry = Object.getOwnPropertyNames(actionHandlers).reduce( (firstBadEntry, type) => {
|
46 | return firstBadEntry || isFunction(actionHandlers[type]) ? null : type;
|
47 | }, null);
|
48 | check(!invalidHashEntry,
|
49 | `actionHandlers['${invalidHashEntry}'] is NOT a function ... expecting reducer function indexed by action type`);
|
50 |
|
51 | check(!actionHandlers['undefined'],
|
52 | "actionHandlers contains an 'undefined' entry ... suspect a misspelled constant");
|
53 |
|
54 |
|
55 | // internal function: locate handler from actionHandlers action.type hash lookup
|
56 | // ... default: identity pass-through
|
57 | const locateHandler = (action) => actionHandlers[action.type] || identity;
|
58 |
|
59 | // expose the new reducer fn, which resolves according the the supplied actionHandlers
|
60 | return (state=initialState, action, originalReducerState) => {
|
61 |
|
62 | // maintain the originalReducerState as the immutable state
|
63 | // at the time of the start of the reduction process
|
64 | // ... in support of joinReducers()
|
65 | // ... for more info, refer to the Dev Guide {{book.guide.originalReducerState}}
|
66 | if (originalReducerState === undefined) {
|
67 | originalReducerState = state;
|
68 | }
|
69 |
|
70 | // execute the handler indexed by the action.type (or the identity pass-through)
|
71 | return locateHandler(action)(state, action, originalReducerState);
|
72 | };
|
73 | }
|
74 |
|
75 |
|
76 | //***
|
77 | //*** Specification: ActionReducerHash
|
78 | //***
|
79 |
|
80 | /**
|
81 | * @typedef {Object} ActionReducerHash
|
82 | *
|
83 | * A hash of reducer functions, indexed by the standard redux
|
84 | * action.type.
|
85 | *
|
86 | * @property {reducerFn} actionType1 - The reducer function servicing: 'actionType1'.
|
87 | * @property {reducerFn} actionType2 - The reducer function servicing: 'actionType2'.
|
88 | * @property {reducerFn} ...more - ...etc.
|
89 | */
|