UNPKG

9.74 kBMarkdownView Raw
1# redux-actions
2
3[![build status](https://img.shields.io/travis/acdlite/redux-actions/master.svg?style=flat-square)](https://travis-ci.org/acdlite/redux-actions)
4
5[![NPM](https://nodei.co/npm/redux-actions.png?downloads=true)](https://nodei.co/npm/redux-actions/)
6
7[Flux Standard Action](https://github.com/acdlite/flux-standard-action) utilities for Redux.
8
9## Installation
10
11```bash
12npm install --save redux-actions
13```
14
15The [npm](https://www.npmjs.com) package provides a [CommonJS](http://webpack.github.io/docs/commonjs.html) build for use in Node.js, and with bundlers like [Webpack](http://webpack.github.io/) and [Browserify](http://browserify.org/). It also includes an [ES modules](http://jsmodules.io/) build that works well with [Rollup](http://rollupjs.org/) and [Webpack2](https://webpack.js.org)'s tree-shaking.
16
17If you don’t use [npm](https://www.npmjs.com), you may grab the latest [UMD](https://unpkg.com/redux-actions@latest/dist) build from [unpkg](https://unpkg.com) (either a [development](https://unpkg.com/redux-actions@latest/dist/redux-actions.js) or a [production](https://unpkg.com/redux-actions@latest/dist/redux-actions.min.js) build). The UMD build exports a global called `window.ReduxActions` if you add it to your page via a `<script>` tag. We *don’t* recommend UMD builds for any serious application, as most of the libraries complementary to Redux are only available on [npm](https://www.npmjs.com/search?q=redux).
18
19## Usage
20
21### `createAction(type, payloadCreator = Identity, ?metaCreator)`
22
23```js
24import { createAction } from 'redux-actions';
25```
26
27Wraps an action creator so that its return value is the payload of a Flux Standard Action.
28
29`payloadCreator` must be a function, `undefined`, or `null`. If `payloadCreator` is `undefined` or `null`, the identity function is used.
30
31Example:
32
33```js
34let increment = createAction('INCREMENT', amount => amount);
35// same as
36increment = createAction('INCREMENT');
37
38expect(increment(42)).to.deep.equal({
39 type: 'INCREMENT',
40 payload: 42
41});
42```
43
44If the payload is an instance of an [Error
45object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Error),
46redux-actions will automatically set ```action.error``` to true.
47
48Example:
49
50```js
51const increment = createAction('INCREMENT');
52
53const error = new TypeError('not a number');
54expect(increment(error)).to.deep.equal({
55 type: 'INCREMENT',
56 payload: error,
57 error: true
58});
59```
60
61`createAction` also returns its `type` when used as type in `handleAction` or `handleActions`.
62
63Example:
64
65```js
66const increment = createAction('INCREMENT');
67
68// As parameter in handleAction:
69handleAction(increment, {
70 next(state, action) {...},
71 throw(state, action) {...}
72});
73
74// As object key in handleActions:
75const reducer = handleActions({
76 [increment]: (state, action) => ({
77 counter: state.counter + action.payload
78 })
79}, { counter: 0 });
80```
81
82**NOTE:** The more correct name for this function is probably `createActionCreator()`, but that seems a bit redundant.
83
84Use the identity form to create one-off actions:
85
86```js
87createAction('ADD_TODO')('Use Redux');
88```
89
90`metaCreator` is an optional function that creates metadata for the payload. It receives the same arguments as the payload creator, but its result becomes the meta field of the resulting action. If `metaCreator` is undefined or not a function, the meta field is omitted.
91
92### `createActions(?actionsMap, ?...identityActions)`
93
94```js
95import { createActions } from 'redux-actions';
96```
97
98Returns an object mapping action types to action creators. The keys of this object are camel-cased from the keys in `actionsMap` and the string literals of `identityActions`; the values are the action creators.
99
100`actionsMap` is an optional object with action types as keys, and whose values **must** be either
101
102- a function, which is the payload creator for that action
103- an array with `payload` and `meta` functions in that order, as in [`createAction`](#createactiontype-payloadcreator--identity-metacreator)
104 - `meta` is **required** in this case (otherwise use the function form above)
105
106`identityActions` is an optional list of positional string arguments that are action type strings; these action types will use the identity payload creator.
107
108```js
109const { actionOne, actionTwo, actionThree } = createActions({
110 // function form; payload creator defined inline
111 ACTION_ONE: (key, value) => ({ [key]: value }),
112
113 // array form
114 ACTION_TWO: [
115 (first) => [first], // payload
116 (first, second) => ({ second }) // meta
117 ],
118
119 // trailing action type string form; payload creator is the identity
120}, 'ACTION_THREE');
121
122expect(actionOne('key', 1)).to.deep.equal({
123 type: 'ACTION_ONE',
124 payload: { key: 1 }
125});
126
127expect(actionTwo('first', 'second')).to.deep.equal({
128 type: 'ACTION_TWO',
129 payload: ['first'],
130 meta: { second: 'second' }
131});
132
133expect(actionThree(3)).to.deep.equal({
134 type: 'ACTION_THREE',
135 payload: 3,
136});
137```
138
139### `handleAction(type, reducer | reducerMap = Identity, defaultState)`
140
141```js
142import { handleAction } from 'redux-actions';
143```
144
145Wraps a reducer so that it only handles Flux Standard Actions of a certain type.
146
147If a `reducer` function is passed, it is used to handle both normal actions and failed actions. (A failed action is analogous to a rejected promise.) You can use this form if you know a certain type of action will never fail, like the increment example above.
148
149Otherwise, you can specify separate reducers for `next()` and `throw()` using the `reducerMap` form. This API is inspired by the ES6 generator interface.
150
151```js
152handleAction('FETCH_DATA', {
153 next(state, action) {...},
154 throw(state, action) {...}
155}, defaultState);
156```
157
158If either `next()` or `throw()` are `undefined` or `null`, then the identity function is used for that reducer.
159
160If the reducer argument (`reducer | reducerMap`) is `undefined`, then the identity function is used.
161
162The third parameter `defaultState` is required, and is used when `undefined` is passed to the reducer.
163
164### `handleActions(reducerMap, defaultState)`
165
166```js
167import { handleActions } from 'redux-actions';
168```
169
170Creates multiple reducers using `handleAction()` and combines them into a single reducer that handles multiple actions. Accepts a map where the keys are passed as the first parameter to `handleAction()` (the action type), and the values are passed as the second parameter (either a reducer or reducer map). The map must not be empty.
171
172The second parameter `defaultState` is required, and is used when `undefined` is passed to the reducer.
173
174(Internally, `handleActions()` works by applying multiple reducers in sequence using [reduce-reducers](https://github.com/acdlite/reduce-reducers).)
175
176Example:
177
178```js
179const reducer = handleActions({
180 INCREMENT: (state, action) => ({
181 counter: state.counter + action.payload
182 }),
183
184 DECREMENT: (state, action) => ({
185 counter: state.counter - action.payload
186 })
187}, { counter: 0 });
188```
189
190### `combineActions(...actionTypes)`
191
192Combine any number of action types or action creators. `actionTypes` is a list of positional arguments which can be action type strings, symbols, or action creators.
193
194This allows you to reduce multiple distinct actions with the same reducer.
195
196```js
197const { increment, decrement } = createActions({
198 INCREMENT: amount => ({ amount }),
199 DECREMENT: amount => ({ amount: -amount }),
200})
201
202const reducer = handleAction(combineActions(increment, decrement), {
203 next: (state, { payload: { amount } }) => ({ ...state, counter: state.counter + amount }),
204 throw: state => ({ ...state, counter: 0 }),
205}, { counter: 10 })
206
207expect(reducer(undefined, increment(1)).to.deep.equal({ counter: 11 })
208expect(reducer(undefined, decrement(1)).to.deep.equal({ counter: 9 })
209expect(reducer(undefined, increment(new Error)).to.deep.equal({ counter: 0 })
210expect(reducer(undefined, decrement(new Error)).to.deep.equal({ counter: 0 })
211```
212
213Here's an example using `handleActions`:
214
215```js
216const { increment, decrement } = createActions({
217 INCREMENT: amount => ({ amount }),
218 DECREMENT: amount => ({ amount: -amount })
219});
220
221const reducer = handleActions({
222 [combineActions(increment, decrement)](state, { payload: { amount } }) {
223 return { ...state, counter: state.counter + amount };
224 }
225}, { counter: 10 });
226
227expect(reducer({ counter: 5 }, increment(5))).to.deep.equal({ counter: 10 });
228expect(reducer({ counter: 5 }, decrement(5))).to.deep.equal({ counter: 0 });
229expect(reducer({ counter: 5 }, { type: 'NOT_TYPE', payload: 1000 })).to.equal({ counter: 5 });
230expect(reducer(undefined, increment(5))).to.deep.equal({ counter: 15 });
231```
232
233## Usage with middleware
234
235redux-actions is handy all by itself, however, its real power comes when you combine it with middleware.
236
237The identity form of `createAction` is a great way to create a single action creator that handles multiple payload types. For example, using [redux-promise](https://github.com/acdlite/redux-promise) and [redux-rx](https://github.com/acdlite/redux-rx):
238
239```js
240const addTodo = createAction('ADD_TODO');
241
242// A single reducer...
243handleAction('ADD_TODO', (state = { todos: [] }, action) => ({
244 ...state,
245 todos: [...state.todos, action.payload]
246}));
247
248// ...that works with all of these forms:
249// (Don't forget to use `bindActionCreators()` or equivalent.
250// I've left that bit out)
251addTodo('Use Redux')
252addTodo(Promise.resolve('Weep with joy'));
253addTodo(Observable.of(
254 'Learn about middleware',
255 'Learn about higher-order stores'
256)).subscribe();
257```
258
259## See also
260
261Use redux-actions in combination with FSA-compliant libraries.
262
263- [redux-promise](https://github.com/acdlite/redux-promise) - Promise middleware
264- [redux-rx](https://github.com/acdlite/redux-rx) - Includes observable middleware.