├── .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 | [![Status](https://travis-ci.org/rstacruz/build-reducer.svg?branch=master)](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 | --------------------------------------------------------------------------------