Updating Objects in State
Overview
State in React is treated as Immutable (read-only). While you can technically mutate a JavaScript object in memory, doing so in React will completely break the app because React won't realize the data changed (React relies on checking if the object reference changed). To update an object in state, you must create a brand new object, copy the old data over using the spread operator (`...`), and apply your new changes.
Syntax
Because `user` points to the exact same spot in memory, React's diffing engine thinks nothing changed and refuses to update the UI.
const [user, setUser] = useState({ name: 'Alice', age: 25 });
const handleBirthday = () => {
// ❌ BAD: Mutating the object directly
user.age = 26;
// setUser(user); // React sees it's the exact same object reference and SKIPS rendering!
};The spread operator `...prevUser` copies all existing fields into a new object. By placing `age: ...` after the spread, we overwrite only the age property.
const [user, setUser] = useState({ name: 'Alice', age: 25 });
const handleBirthday = () => {
// ✅ GOOD: Create a brand new object
// Copy all existing properties using ...user, then overwrite age
setUser(prevUser => ({
...prevUser,
age: prevUser.age + 1
}));
};Common Pitfalls
- Using `push()`, `pop()`, or directly assigning properties (`obj.key = val`). Always treat state as strictly read-only.
- Forgetting to spread the old state `...prev`. If you just do `setUser({ age: 26 })`, you will completely delete the `name` property!
Interview Tips
- The Spread Operator (`...`) only performs a Shallow Copy. If you have deeply nested objects (like `user.address.city`), you must spread EVERY level of nesting.
Real-World Example
Handling complex forms often involves updating specific fields in a single state object.
function ProfileForm() {
const [form, setForm] = useState({ email: "", phone: "" });
// Dynamic handler for multiple inputs
const handleChange = (e) => {
const { name, value } = e.target;
// Compute the key dynamically using [name]
setForm(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<input name="email" onChange={handleChange} />
<input name="phone" onChange={handleChange} />
</form>
);
}