1 | # `redux-immutable`
|
---|
2 |
|
---|
3 | [](https://travis-ci.org/gajus/redux-immutable)
|
---|
4 | [](https://www.npmjs.org/package/redux-immutable)
|
---|
5 | [](https://github.com/gajus/canonical)
|
---|
6 |
|
---|
7 | `redux-immutable` is used to create an equivalent function of Redux [`combineReducers`](http://redux.js.org/docs/api/combineReducers.html) that works with [Immutable.js](https://facebook.github.io/immutable-js/) state.
|
---|
8 |
|
---|
9 | When Redux [`createStore`](https://github.com/reactjs/redux/blob/master/docs/api/createStore.md) `reducer` is created using `redux-immutable` then `initialState` must be an instance of [`Immutable.Collection`](https://facebook.github.io/immutable-js/docs/#/Collection).
|
---|
10 |
|
---|
11 | ## Problem
|
---|
12 |
|
---|
13 | When [`createStore`](https://github.com/reactjs/redux/blob/v3.0.6/docs/api/createStore.md) is invoked with `initialState` that is an instance of `Immutable.Collection` further invocation of reducer will [produce an error](https://github.com/reactjs/redux/blob/v3.0.6/src/combineReducers.js#L31-L38):
|
---|
14 |
|
---|
15 | > The initialState argument passed to createStore has unexpected type of "Object".
|
---|
16 | > Expected argument to be an object with the following keys: "data"
|
---|
17 |
|
---|
18 | This is because Redux `combineReducers` [treats `state` object as a plain JavaScript object](https://github.com/reactjs/redux/blob/v3.0.6/src/combineReducers.js#L120-L129).
|
---|
19 |
|
---|
20 | `combineReducers` created using `redux-immutable` uses Immutable.js API to iterate the state.
|
---|
21 |
|
---|
22 | ## Usage
|
---|
23 |
|
---|
24 | Create a store with `initialState` set to an instance of [`Immutable.Collection`](https://facebook.github.io/immutable-js/docs/#/Collection):
|
---|
25 |
|
---|
26 | ```js
|
---|
27 | import {
|
---|
28 | combineReducers
|
---|
29 | } from 'redux-immutable';
|
---|
30 |
|
---|
31 | import {
|
---|
32 | createStore
|
---|
33 | } from 'redux';
|
---|
34 |
|
---|
35 | const initialState = Immutable.Map();
|
---|
36 | const rootReducer = combineReducers({});
|
---|
37 | const store = createStore(rootReducer, initialState);
|
---|
38 | ```
|
---|
39 |
|
---|
40 | By default, if `state` is `undefined`, `rootReducer(state, action)` is called with `state = Immutable.Map()`. A different default function can be provided as the second parameter to `combineReducers(reducers, getDefaultState)`, for example:
|
---|
41 |
|
---|
42 | ```js
|
---|
43 | const StateRecord = Immutable.Record({
|
---|
44 | foo: 'bar'
|
---|
45 | });
|
---|
46 | const rootReducer = combineReducers({foo: fooReducer}, StateRecord);
|
---|
47 | // rootReducer now has signature of rootReducer(state = StateRecord(), action)
|
---|
48 | // state now must always have 'foo' property with 'bar' as its default value
|
---|
49 | ```
|
---|
50 |
|
---|
51 | When using `Immutable.Record` it is possible to delegate default values to child reducers:
|
---|
52 |
|
---|
53 | ```js
|
---|
54 | const StateRecord = Immutable.Record({
|
---|
55 | foo: undefined
|
---|
56 | });
|
---|
57 | const rootReducer = combineReducers({foo: fooReducer}, StateRecord);
|
---|
58 | // state now must always have 'foo' property with its default value returned from fooReducer(undefined, action)
|
---|
59 | ```
|
---|
60 |
|
---|
61 | In general, `getDefaultState` function must return an instance of `Immutable.Record` or `Immutable.Collection` that implements `get`, `set` and `withMutations` methods. Such collections are `List`, `Map` and `OrderedMap`.
|
---|
62 |
|
---|
63 | ### Using with `react-router-redux`
|
---|
64 |
|
---|
65 | `react-router-redux` [`routeReducer`](https://github.com/reactjs/react-router-redux/tree/v4.0.2#routerreducer) does not work with Immutable.js. You need to use a custom reducer:
|
---|
66 |
|
---|
67 | ```js
|
---|
68 | import Immutable from 'immutable';
|
---|
69 | import {
|
---|
70 | LOCATION_CHANGE
|
---|
71 | } from 'react-router-redux';
|
---|
72 |
|
---|
73 | const initialState = Immutable.fromJS({
|
---|
74 | locationBeforeTransitions: null
|
---|
75 | });
|
---|
76 |
|
---|
77 | export default (state = initialState, action) => {
|
---|
78 | if (action.type === LOCATION_CHANGE) {
|
---|
79 | return state.set('locationBeforeTransitions', action.payload);
|
---|
80 | }
|
---|
81 |
|
---|
82 | return state;
|
---|
83 | };
|
---|
84 | ```
|
---|
85 |
|
---|
86 | Pass a selector to access the payload state and convert it to a JavaScript object via the [`selectLocationState` option on `syncHistoryWithStore`](https://github.com/reactjs/react-router-redux/tree/v4.0.2#history--synchistorywithstorehistory-store-options):
|
---|
87 |
|
---|
88 | ```js
|
---|
89 | import {
|
---|
90 | browserHistory
|
---|
91 | } from 'react-router';
|
---|
92 | import {
|
---|
93 | syncHistoryWithStore
|
---|
94 | } from 'react-router-redux';
|
---|
95 |
|
---|
96 | const history = syncHistoryWithStore(browserHistory, store, {
|
---|
97 | selectLocationState (state) {
|
---|
98 | return state.get('routing').toJS();
|
---|
99 | }
|
---|
100 | });
|
---|
101 | ```
|
---|
102 |
|
---|
103 | The `'routing'` path depends on the `rootReducer` definition. This example assumes that `routeReducer` is made available under `routing` property of the `rootReducer`.
|
---|