Quick answer: useState is the React hook that gives a component memory. Call const [count, setCount] = useState(0) and your component now holds a value that survives re-renders; calling setCount updates it and re-renders the screen. That single pattern powers counters, forms, toggles, tabs and modals. This guide shows the pattern, the update rules that trip people up, and the three classic bugs with fixes.
The pattern: value + setter
A working counter, the “hello world” of state:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
The array destructure names two things: the current value and the function that changes it. useState(0) — the argument is the initial value. Every setter call schedules a re-render, and React replaces the whole component output with the new values. That render-swap is the core mental model of React interactivity.
State update rules that matter
| Rule | Wrong | Right |
|---|---|---|
| Never mutate directly | count = 5 or user.name = 'X' | Always call the setter: setCount(5) |
| New state from old state | setCount(count + 1) twice in a row (stale read) | Updater form: setCount(c => c + 1) |
| Objects/arrays are replaced whole | user.age = 21 | setUser({ ...user, age: 21 }) |
| Setters are async-ish | Reading the variable right after setting | Use the updater form or read it in the next render |
The updater form setCount(c => c + 1) receives the latest value even if two updates happen in one event handler — the direct form reads a stale snapshot. This is the single most common React bug interview question, and now you know it.
Worked example: an input form with state
function SignupForm() {
const [form, setForm] = useState({ name: '', email: '' });
function update(field) {
return (e) => setForm({ ...form, [field]: e.target.value });
}
return (
<form>
<input value={form.name} onChange={update('name')} placeholder="Name" />
<input value={form.email} onChange={update('email')} placeholder="Email" />
<p>Hello {form.name || 'stranger'}, is {form.email} right?</p>
</form>
);
}
One state object for the whole form keeps handlers small: spread the old object, override one field. The live preview paragraph re-renders on every keystroke — you get “controlled input” behavior with two lines per field. Events in detail come next in the series: events and forms, the full guide.
The three classic state bugs
- The invisible update: you mutated an object instead of calling the setter. React compares references — same reference, no re-render. Fix: always produce a new object/array.
- The stale increment: two
setCount(count + 1)calls add up to +1 total. Fix: updater formsetCount(c => c + 1). - The lost state: a component’s state resets unexpectedly — usually its
keyor position in the tree changed. Keys matter so much they get their own guide: lists and keys.
If you want an instructor to review how you structure state before bad habits set in, Ampersand Academy teaches React one-to-one with your own project as the material.
Frequently asked questions
What does useState do in React?
useState gives a component persistent memory. It returns the current value and a setter function; calling the setter stores a new value and triggers a re-render so the screen reflects it.
Why does setCount(count + 1) not add up correctly?
Multiple calls in one handler all read the same stale snapshot of count, so they queue the same result. Use the updater form setCount(c => c + 1) which receives the latest value.
Can I mutate state directly in React?
No. React detects changes by reference comparison. Mutating an object keeps the same reference, so React skips the re-render. Always create a new object or array with the spread syntax and pass it to the setter.
How many useState hooks can a component have?
As many as you need – each call holds one independent piece of state. Many teams group related fields into one object state and keep unrelated values separate.
What is the difference between state and props?
State is owned and changed inside a component; props arrive from the parent and are read-only. A component can pass its state down as props to children.

