Reputation: 488
The redux guide states:
We don't mutate the state. We create a copy with Object.assign(). Object.assign(state, { visibilityFilter: action.filter }) is also wrong: it will mutate the first argument. You must supply an empty object as the first parameter. You can also enable the object spread operator proposal to write { ...state, ...newState } instead.
I happened to catch myself writing the following snippet of working code:
[actions.setSelection](state, {payload}) {
state[payload.key] = payload.value;
return state;
},
I had a bad vibe about it, and revisited the guide for wisdom. I have since rewritten it as this:
[actions.setSelection](state, {payload}) {
const result = Object.assign({}, state);
result[payload.key] = payload.value;
return result;
},
I am fairly sure I have offended the commandment noted above elsewhere in my code. What kind of consequence am I looking at for not tracking them down with diligence?
(Note: The reducer syntax above is via redux-actions. It would otherwise be the block of code in a reducer fn switch/case.)
Upvotes: 15
Views: 10039
Reputation: 1856
If you're using Redux Toolkit's createReducer
or createSlice
to create your reducers, it is safe to mutate the state object inside of the reducer, because within those helper functions, Redux Toolkit uses a tool called Immer to allow you to use mutating syntax without actually mutating the underlying state. It still creates a deep copy instead.
Redux Toolkit's createReducer API uses Immer internally automatically. So, it's already safe to "mutate" state inside of any case reducer function that is passed to createReducer... In turn, createSlice uses createReducer inside, so it's also safe to "mutate" state there as well.
(Incidentally, Immer itself is a neat util that can be handy if you're working with deep copies outside of Redux)
Upvotes: 1
Reputation: 2934
Mutating state is an anti-pattern in React. React uses a rendering engine which depends on the fact that state changes are observable. This observation is made by comparing previous state with next state. It will alter a virtual dom with the differences and write changed elements back to the dom.
When you alter the internal state, React does not know what's changed, and even worse; it's notion of the current state is incorrect. So the dom and virtual dom will become out of sync.
Redux uses the same idea to update it's store; an action can be observed by reducers which calculate the next state of the store. Changes are emitted and for example consumed by react-redux connect
.
So in short: never ever, mutate state. Instead of Object.assign you can use the stage-3 spread syntax:
{...previousState,...changes}
Also please note, this is true for arrays as well!
Upvotes: 17