useState — Memory for a Component
The like button on each book should count clicks. A plain let likes = 0 inside the component will not work: every render calls the function again and likes starts from zero. And even if you changed it, React would not know to redraw. A component needs memory that survives renders and tells React when it changes. That is state.
The rule
State is a snapshot; set it to a new value, never change it.
import { useState } from 'react';
function LikeButton() {
const [likes, setLikes] = useState(0);
return (
<button onClick={() => setLikes(likes + 1)}>
♥ {likes}
</button>
);
}useState(0) returns a pair: the current value and a setter. The 0 is only used on the first render; after that React remembers the value. Calling the setter stores a new value and triggers a re-render, where likes holds the new number.
A snapshot. Within one render, likes is a constant. Calling setLikes does not change the variable you are holding; it schedules the next render. So this adds only 1:
setLikes(likes + 1); // 0 + 1
setLikes(likes + 1); // still 0 + 1
setLikes(likes + 1); // still 0 + 1When the next value depends on the previous one, pass an updater function. React feeds each one the latest pending value:
setLikes(l => l + 1); // three of these add 3Set a new value, never change it. For objects and arrays, make a new copy with the change:
setBooks([...books, newBook]); // add
setBooks(books.filter(b => b.id !== id)); // remove
setBooks(books.map(b => b.id === id ? { ...b, read: true } : b)); // editbooks.push(newBook) changes the old array in place; React sees the same array and may skip the update.
Props or state? Props are handed to you by a parent and are read-only. State is kept by the component itself and changes over time. Each <LikeButton /> on the page has its own separate likes. If two components need the same changing value, move the state up to their closest shared parent and pass it down as props.
The shortcut
Before writing a setter, ask two questions: "Does the new value depend on the old one?" If yes, use an updater. "Is it an object or array?" If yes, build a new one.
Read the snapshot
This render has
likes = 5.
Drills
Answer fast — trust the rule.
With likes = 2, a handler calls setLikes(likes + 1) twice. What does the next render show?
With likes = 2, a handler calls setLikes(l => l + 1) twice. What does the next render show?
Which correctly adds newBook to books state?
Right after setLikes(10), the next line logs likes. What does it print if likes was 4?
Two sibling components must show the same changing count. Where should the state live?
Remember
- State is a snapshot; set it to a new value, never change it.
- Depends on the old value? Use an updater. Object or array? Build a new one.
- Trap:
setX(x + 1)repeated, andpushon state arrays.
