Navigate
Search topics, hooks, and fundamentals
React compares state by reference using Object.is(). If you mutate the same object, React thinks nothing changed — and skips the re-render.
When you call setState, React compares the old and new values using Object.is(). For objects, this checks if they're the same reference — not whether the contents match:
// React uses Object.is() to compare state
Object.is(3, 3); // true → no re-render
Object.is("hello", "hello"); // true → no re-render
Object.is(oldObj, newObj); // false → RE-RENDER!
Object.is(oldObj, oldObj); // true → no re-render (mutation trap!)This is what happens when you mutate state directly. The data changes in memory, but React never knows about it:
function PotEditor() {
const [pot, setPot] = useState({ color: "red", size: "large" });
const handleChange = () => {
pot.color = "blue"; // MUTATION! Same reference
setPot(pot); // React: Object.is(old, new) → true
// "Nothing changed" → no re-render!
};
return <div>{pot.color}</div>; // still shows "red"
}Try both approaches — mutation (broken) and immutable update (works). The mutation changes the data but the screen never updates:
pot.color = "red"
renders: 1
The color changes in memory, but the screen doesn't update because React sees the same object reference.
pot.color = "red"
renders: 1
New object = new reference = React re-renders and shows the update.
Always create a new object when updating state. The spread operator is the simplest way:
function PotEditor() {
const [pot, setPot] = useState({ color: "red", size: "large" });
const handleChange = () => {
setPot({
...pot, // copy existing properties
color: "blue", // override what changed
});
// New object → new reference → React re-renders ✅
};
return <div>{pot.color}</div>; // shows "blue"
}Object.is() to compare old vs new state