State: a component's private memory
useState fixes both. It
returns a pair [value, setValue]: value is React's
memory for this component instance (it survives re-renders), and
setValue tells React "re-render me with this new value" — it
does NOT mutate the variable in place. The render loop:
render →
user clicks →
setX(next) →
React re-renders →
fresh JSX snapshot →
paint.
| piece | role | what it does |
|---|---|---|
useState(0) |
declare state | gives this instance a memory slot; first render returns 0 |
count (1st of the pair) |
the snapshot | the value for this render; it never changes mid-render, even after setCount |
setCount (2nd of the pair) |
request a re-render | queues a new value; React re-renders and hands the next render the new value |
setCount(c => c+1) |
functional update | computes from the pending state; safe to call several times in one handler (they queue up) |
React 19.2.7 (esm.sh) + Babel classic — the same CDN setup as react_via_cdn. Edit the JSX below and hit compile & render.
1 · the Counter you write (edit me)
2 · Babel compiles JSX into an element tree
<Counter/> becomes React.createElement(Counter) — a
plain object describing the node. React walks that tree and produces real DOM.
Each setCount/setOn asks React to call
Counter again and paint a new snapshot.
// (hit “compile & render” to see Babel’s output here)
3 · live React state (the re-render model, proven)
Click the buttons in the card — that is live React state. The gold-check
proves it end-to-end: it programmatically clicks +1 5 times
(real click() events) and asserts the rendered count is "5";
then it clicks +3 once and asserts "8". The number you see
below IS the gold value — it changed because React re-rendered on each click.
rendered count: —
· toggle: —
· gold clicks: 5 × +1, then 1 × +3 → expect count 8
intent → pattern (the cheat sheet, live)
| intent | pattern | why |
|---|---|---|
| add 1 from the previous value | setCount(c => c + 1) |
functional update uses the pending state; survives batching |
| set to a brand-new value | setName('Robin') |
next value doesn't depend on the old one |
| flip a boolean (toggle) | setOn(o => !o) |
negate the pending value |
| reset to initial | setCount(0) |
or remount with a new key to wipe all state at once |