
Immutability and Structural Sharing
React relies on reference equality to detect changes. Mutating state directly breaks this — React can’t see the change and skips the re-render. Structural sharing is how you update immutably without copying the entire state tree.
Why Immutability
// Bug: mutating state directly
const [items, setItems] = useState([{ id: 1, name: "Apple" }]);
function updateItem(name) {
items[0].name = name; // mutates the existing array
setItems(items); // same reference — React bails out, no re-render
} React compares prevState === nextState. Same reference → equal → no render.
Correct Update Patterns
Create new references for changed parts only:
// Update a field in an object
setUser((prev) => ({ ...prev, name: "Alice" }));
// Add to an array
setItems((prev) => [...prev, newItem]);
// Remove from an array
setItems((prev) => prev.filter((item) => item.id !== idToRemove));
// Update one item in an array
setItems((prev) =>
prev.map((item) =>
item.id === targetId ? { ...item, name: "Updated" } : item,
),
); Structural Sharing
You don’t copy everything — only the parts that changed. Unchanged subtrees keep their original references.
const state = {
users: [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
],
settings: { theme: "dark" },
};
// Update only user 1's name
const nextState = {
...state, // settings keeps the same reference
users: state.users.map(
(u) => (u.id === 1 ? { ...u, name: "Alicia" } : u), // user 2 keeps same reference
),
};
nextState.settings === state.settings; // true — reused
nextState.users[1] === state.users[1]; // true — reused Unchanged references allow React.memo and useMemo to skip re-renders for those subtrees.
Deep Nesting
Immutable updates on deeply nested state are verbose:
const next = {
...state,
a: {
...state.a,
b: {
...state.a.b,
value: "new",
},
},
}; Use Immer to write mutable-looking code that produces immutable results:
import produce from "immer";
const next = produce(state, (draft) => {
draft.a.b.value = "new"; // Immer handles the copying
}); useImmer integrates this with useState directly.









