1 | # astx-redux-util
|
2 |
|
3 | The [astx-redux-util] library promotes several redux reducer
|
4 | composition utilities, which blend multiple reducers together forming
|
5 | a richer abstraction through functional decomposition
|
6 | (i.e. higher-order functions).
|
7 |
|
8 | Reducer composition is not new. Redux itself provides the innovative
|
9 | [combineReducers](http://redux.js.org/docs/api/combineReducers.html)
|
10 | utility which allows you to fuse individual reducers together to build
|
11 | up the overall shape of your application state.
|
12 |
|
13 | The most prevalent [astx-redux-util] utility is **reducerHash()**,
|
14 | which lets you combine sub-reducers in such a way as to eliminate
|
15 | the switch statement commonly used to delineate action type.
|
16 |
|
17 | **Additionally**, [astx-redux-util] promotes other reducer compositions that
|
18 | can be used in conjunction with one another.
|
19 |
|
20 |
|
21 | [![Build Status](https://travis-ci.org/KevinAst/astx-redux-util.svg?branch=master)](https://travis-ci.org/KevinAst/astx-redux-util)
|
22 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b7e9e537a56e41a692aef023fd15d9ca)](https://www.codacy.com/app/KevinAst/astx-redux-util?utm_source=github.com&utm_medium=referral&utm_content=KevinAst/astx-redux-util&utm_campaign=Badge_Grade)
|
23 | [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/b7e9e537a56e41a692aef023fd15d9ca)](https://www.codacy.com/app/KevinAst/astx-redux-util?utm_source=github.com&utm_medium=referral&utm_content=KevinAst/astx-redux-util&utm_campaign=Badge_Coverage)
|
24 | [![Known Vulnerabilities](https://snyk.io/test/github/kevinast/astx-redux-util/badge.svg)](https://snyk.io/test/github/kevinast/astx-redux-util)
|
25 | [![NPM Version Badge](https://img.shields.io/npm/v/astx-redux-util.svg)](https://www.npmjs.com/package/astx-redux-util)
|
26 |
|
27 | ## Comprehensive Documentation
|
28 |
|
29 | Complete documentation can be found at
|
30 | https://astx-redux-util.js.org/, which includes both **API** details,
|
31 | and a **Dev Guide** with full and thorough **examples**!
|
32 |
|
33 |
|
34 | ## Install
|
35 |
|
36 | ```shell
|
37 | npm install --save astx-redux-util
|
38 | ```
|
39 |
|
40 |
|
41 | ## Examples
|
42 |
|
43 | ### Basics
|
44 |
|
45 | The following example uses **reducerHash()** to combine a set of
|
46 | sub-reducer functions (indexed by the standard action.type),
|
47 | eliminating the switch statement commonly used to delineate action
|
48 | type.
|
49 |
|
50 | **Don't miss the [astx-redux-util] documentation**, *which fully explores
|
51 | this example, and details the API.*
|
52 |
|
53 | ```js
|
54 | import { reducerHash } from 'astx-redux-util';
|
55 |
|
56 | export default reducerHash({
|
57 | "widget.edit": (widget, action) => action.widget,
|
58 | "widget.edit.close": (widget, action) => null,
|
59 | }, null);
|
60 | ```
|
61 |
|
62 |
|
63 | ### Joining Reducers
|
64 |
|
65 | Building on the previous example, our widget now takes on more detail:
|
66 | - we manage x/y properties (through the standard
|
67 | [combineReducers](http://redux.js.org/docs/api/combineReducers.html))
|
68 | - the widget itself can take on a null value (an indication it is NOT
|
69 | being edited)
|
70 |
|
71 | We manage these new requirements by combining multiple reducers
|
72 | through a functional decomposition (as opposed to procedural code).
|
73 | To accomplish this, we add to our repertoire by introducing
|
74 | **joinReducers()** and **conditionalReducer()**.
|
75 |
|
76 | **Did I mention that the [astx-redux-util] documentation**, *fully explores
|
77 | this example, and details the API?*
|
78 |
|
79 | ```js
|
80 | import * as Redux from 'redux';
|
81 | import * as AstxReduxUtil from 'astx-redux-util';
|
82 | import x from '../appReducer/x';
|
83 | import y from '../appReducer/y';
|
84 |
|
85 | export default AstxReduxUtil.joinReducers(
|
86 | // FIRST: determine content shape (i.e. {} or null)
|
87 | AstxReduxUtil.reducerHash({
|
88 | "widget.edit": (widget, action) => action.widget,
|
89 | "widget.edit.close": (widget, action) => null
|
90 | }),
|
91 |
|
92 | AstxReduxUtil.conditionalReducer(
|
93 | // SECOND: maintain individual x/y fields
|
94 | // ONLY when widget has content (i.e. is being edited)
|
95 | (widget, action, originalReducerState) => widget !== null,
|
96 | Redux.combineReducers({
|
97 | x,
|
98 | y
|
99 | })),
|
100 |
|
101 | null); // initialState
|
102 | ```
|
103 |
|
104 | ### Full Example
|
105 |
|
106 | Building even more on the prior examples:
|
107 | - our widget adds a curHash property (which is a determinate of
|
108 | whether application content has changed)
|
109 |
|
110 | We manage this new property in the parent widget reducer, because it
|
111 | has a unique vantage point of knowing when the widget has changed
|
112 | (under any circumstance, regardless of how many properties are
|
113 | involved).
|
114 |
|
115 | We accomplish this by simply combining yet another reducer (using a
|
116 | functional approach). This also demonstrates how **composition can be
|
117 | nested!**
|
118 |
|
119 | **Read all about it! The [astx-redux-util] documentation** *fully explores
|
120 | this example, and details the API.*
|
121 |
|
122 | ```js
|
123 | import * as Redux from 'redux';
|
124 | import * as AstxReduxUtil from 'astx-redux-util';
|
125 | import x from '../appReducer/x';
|
126 | import y from '../appReducer/y';
|
127 | import Widget from '../appReducer/Widget';
|
128 |
|
129 | export default AstxReduxUtil.joinReducers(
|
130 | // FIRST: determine content shape (i.e. {} or null)
|
131 | AstxReduxUtil.reducerHash({
|
132 | "widget.edit": (widget, action) => action.widget,
|
133 | "widget.edit.close": (widget, action) => null
|
134 | }),
|
135 |
|
136 | AstxReduxUtil.conditionalReducer(
|
137 | // NEXT: maintain individual x/y fields
|
138 | // ONLY when widget has content (i.e. is being edited)
|
139 | (widget, action, originalReducerState) => widget !== null,
|
140 | AstxReduxUtil.joinReducers(
|
141 | Redux.combineReducers({
|
142 | x,
|
143 | y,
|
144 | curHash: (s=null)=>s // defaulted state placebo reducer (needed by combineReducers())
|
145 | }),
|
146 | AstxReduxUtil.conditionalReducer(
|
147 | // LAST: maintain curHash
|
148 | // ONLY when widget has content (see condition above) -AND- has changed
|
149 | (widget, action, originalReducerState) => originalReducerState !== widget,
|
150 | (widget, action) => {
|
151 | widget.curHash = Widget.hash(widget); // OK to mutate (because of changed instance)
|
152 | return widget;
|
153 | })
|
154 | )
|
155 | ), null); // initialState
|
156 | ```
|
157 |
|
158 | This represents a very comprehensive example of how **Reducer
|
159 | Composition** can **simplify your life**! We have combined multiple
|
160 | reducers into one, applying conditional logic as needed through
|
161 | functional decomposition!
|
162 |
|
163 |
|
164 | [astx-redux-util]: https://astx-redux-util.js.org/
|