redux-updeep
is a small reducer generator that uses updeep to immutably deep merge partial updates into the reducer's state. It's great for reducing boilerplate in your redux actions and reducers!
redux-updeep
is available on npm:
npm install redux-updeep
import createReducer from 'redux-updeep';
const initialState = {
user: {
name: 'Alex',
company: 'Algolia'
}
};
export default createReducer('MY_REDUCER', initialState);
That's it ! No need to specify any actions, redux-updeep
will take care of it.
In the previous example, MY_REDUCER
is the namespace of the reducer. The reducer automatically handle any action whose type
starts with MY_REDUCER
and immutable deep merge the payload
of the action into the reducer state.
export function updateName(newName) {
return {
type: 'MY_REDUCER/UPDATE_NAME',
payload: {
user: {
name: newName
}
}
};
}
dispatch(updateName('Alexandre'))
// New State:
{
user: {
name: 'Alexandre',
company: 'Algolia'
}
}
Deep merging arrays using updeep can lead to surprising behaviour, therefore it's recommended to use updeep.constant
when merging arrays as values.
Updeep provides a number of utility functions which are very useful for handling arrays, for instance updeep.reject
.
import createReducer from 'redux-updeep';
import u from 'updeep';
const initialState = {
user: {
name: 'Alex',
company: 'Algolia',
favoriteColors: ['green', 'rebeccapurple']
}
};
export default createReducer('MY_REDUCER', initialState);
export function removeFavoriteColor(newFavoriteColors) {
return {
type: 'MY_REDUCER/UPDATE_FAVORITE_COLORS',
payload: {
user: {
// Will ensure the new array replaces the previous one instead of merging with it
favoriteColor: u.constant(newFavoriteColors)
}
}
};
}
export function removeFavoriteColor(colorToRemove) {
return {
type: 'MY_REDUCER/REMOVE_FAVORITE_COLOR',
payload: {
user: {
favoriteColor: u.reject(item => item === colorToRemove)
}
}
};
}
Sometimes deep merging partial updates is not enough and we need to perform complex transformations of the state. In this case, it is possible to pass a third argument to the createReducer
function, which needs to be an object mapping action types to normal reducers.
export default createReducer('MY_REDUCER', initialState, {
'MY_REDUCER/COMPLEX_ACTION': (state, action) => {
return complexTransformation(state, action.payload);
};
})
It is possible to specify a path
at which the payload should be merged inside the object. Those paths are the same as the _.get
paths and can be strings or arrays.
import createReducer from 'redux-updeep';
import {reject} from 'updeep';
const initialState = {
user: {
name: 'Alex',
company: 'Algolia',
}
};
export default createReducer('MY_REDUCER', initialState);
export function updateName(newName) {
return {
type: 'MY_REDUCER/UPDATE_FAVORITE_COLORS',
payload: newName,
path: ['user', 'name']
};
}