As you learned earlier in this lesson, a reducer must never mutate its arguments. If the state changes, the reducer must return a new object.
JavaScript provides us with an easy way to enforce this. Object.freeze
prevents new properties from being added to an object, and also prevents properties currently on an object from being altered or deleted. Essentially, it renders an object immutable, which is exactly what you want.
When you finish this article, you should be able to use Object.freeze
to prevent the current state within a reducer from being mutated.
Object.freeze
to prevent state mutationsBy calling Object.freeze(state)
at the top of every reducer, you can ensure that the state is never accidentally mutated. For example, this is what your farmer reducer from the Fruit Stand application would look like:
const farmersReducer = (state = {}, action) => {
Object.freeze(state);
let nextState = Object.assign({}, state);
switch (action.type) {
case HIRE_FARMER:
const farmerToHire = {
id: action.id,
name: action.name,
paid: false
};
nextState[action.id] = farmerToHire;
return nextState;
case PAY_FARMER:
const farmerToPay = nextState[action.id];
farmerToPay.paid = !farmerToPay.paid;
return nextState;
default:
return state;
}
};
Now you can be certain that you won’t accidentally mutate the state within the reducer.
Here’s another example:
When you try to mutate an object that you froze by modifying a property, it will be prevented:
Note: This is not a deep freeze. Object.freeze
performs a shallow freeze as it only applies to the immediate properties of the object itself. Nested objects can still be mutated, so be careful. Here’s an example of this:
Object.freeze
and arraysYou can also use Object.freeze
to freeze an array, so if a reducer’s state
parameter is an array, you can still prevent accidental state mutations:
const fruitReducer = (state = [], action) => {
Object.freeze(state);
switch (action.type) {
case ADD_FRUIT:
return [...state, action.fruit];
case ADD_FRUITS:
return [...state, ...action.fruits];
case SELL_FRUIT:
const index = state.indexOf(action.fruit);
if (index !== -1) {
// remove first instance of action.fruit
return [...state.slice(0, index), ...state.slice(index + 1)];
}
return state; // if action.fruit is not in state, return previous state
case SELL_OUT:
return [];
default:
return state;
}
};
When an array is frozen with Object.freeze
, its elements cannot be altered and no elements can be added to or removed from the array. Just like with objects, freezing arrays has limitations. If the array’s elements containing objects, properties on those objects can still be mutated.
In this article, you learned how to use Object.freeze
to prevent the current state within a reducer from being mutated.