├── .gitignore
├── .travis.yml
├── HISTORY.md
├── README.md
├── index.js
├── package.json
└── test.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '6'
4 |
--------------------------------------------------------------------------------
/HISTORY.md:
--------------------------------------------------------------------------------
1 | ## [v1.2.0]
2 | > Aug 24, 2016
3 |
4 | - Secure from exploits of action types based on Object builtins.
5 | - Don't invoke sub-reducers if they aren't functions. (thanks [@victorsolis])
6 |
7 | [v1.2.0]: https://github.com/rstacruz/build-reducer/compare/v1.1.0...v1.2.0
8 |
9 | ## [v1.1.0]
10 | > Aug 24, 2016
11 |
12 | - Support default states. (thanks [@marksteve])
13 |
14 | [v1.1.0]: https://github.com/rstacruz/build-reducer/compare/v1.0.0...v1.1.0
15 |
16 | ## [v1.0.0]
17 | > Aug 24, 2016
18 |
19 | - Initial release.
20 |
21 | [v1.0.0]: https://github.com/rstacruz/build-reducer/tree/v1.0.0
22 | [@victorsolis]: https://github.com/victorsolis
23 | [@marksteve]: https://github.com/marksteve
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # build-reducer
2 |
3 | > Write Redux reducers with simpler syntax
4 |
5 | build-reducer lets you write [Redux] reducers as individual functions, rather than one huge `switch` block.
6 |
7 | [](https://travis-ci.org/rstacruz/build-reducer "See test builds")
8 |
9 |
10 | With build-reducer, writing reducers is fun using the ES2015 syntax!
11 |
12 | ```js
13 | import buildReducer from 'build-reducer'
14 | import {createStore} from 'redux'
15 |
16 | const reducer = buildReducer({
17 | reset () {
18 | return {}
19 | },
20 | 'profile:load' (state, {payload}) {
21 | return { ...state, profile: payload }
22 | },
23 | 'profile:reset' (state, action) {
24 | return { ...state, profile: {} }
25 | }
26 | })
27 |
28 | let store = createStore(reducer)
29 | ```
30 |
31 |
32 |
33 | If you were to write this without build-reducer, you'd have to use a big `switch` block.
34 |
35 | ```js
36 | /* Traditional Redux reducer without build-reducer */
37 | function reducer (state, action) {
38 | switch (action.type) {
39 | case 'reset':
40 | return {}
41 | case 'profile:load':
42 | return { ...state, profile: action.payload }
43 | case 'profile:reset':
44 | return { ...state, profile: {} }
45 | default:
46 | return state
47 | }
48 | }
49 |
50 | let store = createStore(reducer)
51 | ```
52 |
53 |
54 |
55 |
56 | ## Install
57 |
58 | ```
59 | npm install --save build-reducer
60 | ```
61 |
62 | build-reducer is available via npm.
63 |
64 | ```js
65 | var buildReducer = require('build-reducer') // ES5
66 | import buildReducer from 'build-reducer' // ES2015+
67 | ```
68 |
69 |
70 |
71 | ## API
72 |
73 | ### buildReducer
74 |
75 | > `buildReducer(reducer, [defaultState])`
76 |
77 | Creates a function that calls methods from `reducer` based on the given action type.
78 |
79 | `defaultState` is optional; if given, it will be used as the state if the state is currently `undefined`.
80 |
81 |
82 |
83 | ## More examples
84 |
85 |
86 | You can use the implicit return arrow syntax if you have very simple functions.
87 |
88 | ```js
89 | const reducer = buildReducer({
90 | 'reset':
91 | () => ({})
92 | 'profile:load':
93 | (state, {payload}) => ({ ...state, profile: payload })
94 | 'profile:reset':
95 | (state, action) => ({ ...state, profile: {} })
96 | })
97 | ```
98 |
99 |
100 |
101 | If you prefer to use `CONSTANTS` instead of strings, you can do that with ES2015's computed property names syntax.
102 |
103 | ```js
104 | const RESET = 'RESET'
105 | const LOAD_PROFILE = 'LOAD_PROFILE'
106 | const RESET_PROFILE = 'RESET_PROFILE'
107 |
108 | const reducer = buildReducer({
109 | [RESET] () {
110 | return {}
111 | },
112 | [LOAD_PROFILE] (state, {payload}) {
113 | return { ...state, profile: payload }
114 | },
115 | [RESET_PROFILE] (state, action) {
116 | return { ...state, profile: {} }
117 | }
118 | })
119 | ```
120 |
121 |
122 |
123 | build-reducer doesn't need ES2015. You can write your reducers in plain ES5.
124 |
125 | ```js
126 | const reducer = buildReducer({
127 | 'reset': function () {
128 | return {}
129 | },
130 | 'profile:load': function (state, action) {
131 | return Object.assign({}, state, { profile: action.payload })
132 | },
133 | 'profile:reset': function (state, action) {
134 | return Object.assign({}, state, { profile: {} })
135 | }
136 | })
137 | ```
138 |
139 |
140 | [Redux]: http://redux.js.org
141 |
142 |
143 |
144 | ## Thanks
145 |
146 | **build-reducer** © 2016+, Rico Sta. Cruz. Released under the [MIT] License.
147 | Authored and maintained by Rico Sta. Cruz with help from contributors ([list][contributors]).
148 |
149 | > [ricostacruz.com](http://ricostacruz.com) ·
150 | > GitHub [@rstacruz](https://github.com/rstacruz) ·
151 | > Twitter [@rstacruz](https://twitter.com/rstacruz)
152 |
153 | [MIT]: http://mit-license.org/
154 | [contributors]: http://github.com/rstacruz/build-reducer/contributors
155 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function buildReducer (reducers, defaultState) {
2 | var useDefaultState = arguments.length > 1
3 |
4 | return function (state, action) {
5 | if (useDefaultState && state === undefined) {
6 | state = defaultState
7 | }
8 |
9 | if (!reducers.hasOwnProperty(action.type)) return state
10 |
11 | var fn = reducers[action.type]
12 | if (typeof fn !== 'function') return state
13 |
14 | return fn(state, action)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "build-reducer",
3 | "description": "Write Redux reducers with simpler syntax",
4 | "version": "1.2.0",
5 | "author": "Rico Sta. Cruz (http://ricostacruz.com)",
6 | "bugs": {
7 | "url": "https://github.com/rstacruz/build-reducer/issues"
8 | },
9 | "devDependencies": {
10 | "tape": "4.6.0"
11 | },
12 | "homepage": "https://github.com/rstacruz/build-reducer#readme",
13 | "keywords": [
14 | "compose",
15 | "reducer",
16 | "redux"
17 | ],
18 | "license": "MIT",
19 | "main": "index.js",
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/rstacruz/build-reducer.git"
23 | },
24 | "scripts": {
25 | "test": "tape test.js"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var test = require('tape')
2 | var build = require('./index')
3 |
4 | var output
5 | var NIL = { passthru: true }
6 |
7 | test('buildReducer()', function (t) {
8 | var red = build({
9 | 'reset': function () {
10 | return 0
11 | },
12 | 'add': function (state, action) {
13 | return state + action.payload
14 | }
15 | })
16 |
17 | output = red({}, { type: 'reset' })
18 | t.equal(output, 0, '"reset" action')
19 |
20 | output = red(5, { type: 'add', payload: 10 })
21 | t.equal(output, 15, '"add" action')
22 |
23 | output = red(NIL, { type: '@@redux/INIT' })
24 | t.equal(output, NIL, 'passthru')
25 |
26 | output = red(NIL, { type: 'toString' })
27 | t.equal(output, NIL, 'dont call builtins')
28 |
29 | t.end()
30 | })
31 |
32 | test('default states', function (t) {
33 | var red = build({
34 | 'add': function (state, action) {
35 | return state + action.payload
36 | }
37 | }, 100)
38 |
39 | output = red(undefined, { type: 'add', payload: 2 })
40 | t.equal(output, 102)
41 |
42 | output = red(null, { type: 'add', payload: 2 })
43 | t.equal(output, null + 2)
44 |
45 | output = red(200, { type: 'add', payload: 2 })
46 | t.equal(output, 202)
47 |
48 | t.end()
49 | })
50 |
--------------------------------------------------------------------------------