utilities for working with redux and immutable.js
npm install mindfront-redux-utils-immutable




If you are building for legacy browsers with webpack or a similar bundler, make
sure to add a rule to transpile this package to ES5.
``es6`
import { combineReducers } from 'mindfront-redux-utils-immutable'
`es6`
function combineReducers(
reducers: {[key: any]: Reducer},
createInitialState?: (initialValues: Object) => Immutable.Collection.Keyed | Record = Immutable.Map
): Reducer
Like combineReducers from redux, except that the combined reducer operates on immutable keyed collections or
records.
Unlike the combineReducers implementation from the redux-immutable package, this implementation isactionHandlers
highly optimized. If multiple reducers have , they will be recombined so that all mutations forstate.withMutations
one of those actions are done inside a single call.
As an example, let's say we have the following reducer:
`js
const setUser = (oldUser, action) => action.newUser
const updateUser = (user, action) => user.merge(action.payload)
const incUserChangeCount = (count = 0) => count + 1
const reducer = combineReducers({
user: createReducer({
[SET_USER]: setUser,
[UPDATE_USER]: updateUser,
}),
userChangeCount: createReducer({
[SET_USER]: incUserChangeCount,
}),
})
`
combineReducers essentially works like the following:
`js`
const reducer = createReducer({
[SET_USER]: (state, action) =>
state.withMutations((state) => {
state.update('user', (oldUser) => setUser(oldUser, action))
state.update('userChangeCount', (count) =>
incUserChangeCount(count, action)
)
}),
[UPDATE_USER]: (state, action) =>
state.update('user', (user) => updateUser(user, action)),
})
Due to this optimization, unlike other combineReducers implementations, if you call the combined reducer with an
empty collection, it will not set the initial values for any reducers skipped by optimization. For example:
`es6
import { Map } from 'immutable'
import { createReducer } from 'mindfront-redux-utils'
import { combineReducers } from 'mindfront-redux-utils-immutable'
const reducer = combineReducers({
a: createReducer(0, {
a: (state) => state + 1,
ab: (state) => state + 1,
}),
b: createReducer(0, {
ab: (state) => state + 1,
b: (state) => state + 1,
}),
})
reducer(undefined, { type: 'a' }) // Map({a: 1, b: 0})
reducer(Map(), { type: 'a' }) // Map({a: 1})
reducer(Map(), { type: 'b' }) // Map({b: 1})
reducer(Map(), { type: 'ab' }) // Map({a: 1, b: 1})
`
As long as you handle missing keys everywhere in your code, this is not a problem.
Another workaround is to use Records with the desired initial values in the Record constructor:
`es6
import { Record } from 'immutable'
import { createReducer } from 'mindfront-redux-utils'
import { combineReducers } from 'mindfront-redux-utils-immutable'
const MyRecord = Record({ a: 0, b: 0 })
const reducer = combineReducers(
{
a: createReducer(0, {
a: (state) => state + 1,
ab: (state) => state + 1,
}),
b: createReducer(0, {
ab: (state) => state + 1,
b: (state) => state + 1,
}),
},
MyRecord
)
reducer(undefined, { type: 'a' }) // MyRecord({a: 1, b: 0})
reducer(MyRecord(), { type: 'a' }) // MyRecord({a: 1, b: 0})
reducer(MyRecord(), { type: 'b' }) // MyRecord({b: 1, b: 0})
reducer(MyRecord(), { type: 'ab' }) // MyRecord({a: 1, b: 1})
`
`es6`
import { subpathReducer } from 'mindfront-redux-utils-immutable'
`es6`
function subpathReducer(
path: Array
initialState?: Immutable.Collection.Keyed | Record
): Reducer => Reducer
Creates a reducer that applies the decorated reducer at the given path within the state. This is basically(state = initialState, action) => state.updateIn(path, reducer)
equivalent to except that if the decorated reduceractionHandlers
has , the created reducer will have corresponding actionHandlers so that other utils can optimize it.
If you pass a function for path, subpathReducer will get the actual path by calling path(action) for eachaction`.