Loading lessons...
State Immutability Rules
State Immutability Rules
In React, state must be treated as immutable (read-only). You should never modify existing state objects or arrays directly.
Why Immutability Matters
React determines whether a component needs to re-render by comparing object memory references (Object.is(oldState, newState)).
If you mutate an existing array or object directly, its memory reference remains identical. React assumes nothing has changed and skips re-rendering.
// ❌ WRONG: Mutating state directly!
const [items, setItems] = useState(['Apple', 'Banana']);
function addItemBad() {
items.push('Cherry'); // Mutates existing array in memory!
setItems(items); // Same reference! React will NOT re-render!
}
// ✅ CORRECT: Creating a new copy using ES6 spread operator (...)
function addItemGood() {
setItems([...items, 'Cherry']); // New array reference created!
}
Updating Arrays Immutably
| Operation | ❌ Mutable Method (Avoid) | ✅ Immutable Alternative |
|---|---|---|
| Add item | arr.push(item) | [...arr, item] |
| Prepend item | arr.unshift(item) | [item, ...arr] |
| Remove item | arr.splice(index, 1) | arr.filter(item => ...) |
| Modify item | arr[index] = val | arr.map(item => ...) |
| Sort array | arr.sort() | [...arr].sort() |
// Example: Removing an item immutably with filter
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: "Buy groceries" },
{ id: 2, text: "Walk dog" }
]);
const deleteTodo = (idToDelete) => {
setTodos(todos.filter(todo => todo.id !== idToDelete));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
{todo.text}
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
);
}
Summary & TL;DR
- Always treat React state as immutable.
- Never use mutating methods like
push(),pop(),splice(), or direct assignment on state. - Create new copies of arrays and objects using spread syntax (
...),filter(), andmap().