Blog
3 min read

React useState Explained: How State Actually Works

useState gives a component memory that survives re-renders and updates the screen when it changes. How it works, why the value doesn't change immediately after you set it, updating objects and arrays without mutating them, and the functional update form.

useState is the first React hook everyone meets and the source of most beginner confusion. Once you understand three rules, almost every "why isn't my state updating?" bug becomes obvious.

What state is

A React component is a function that runs every time the screen needs updating. Normal variables inside it are reset on every run:

function Counter() {
  let count = 0          // reset to 0 on every render
  return <button onClick={() => count++}>{count}</button>   // never changes on screen
}

State is a variable React remembers between runs. And when you change it, React re-runs the component so the screen shows the new value.

function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}

useState(0) returns two things: the current value (count) and a function to change it (setCount). 0 is the starting value, used only on the first render.

Rule 1: Setting state doesn't change the variable right now

function handleClick() {
  setCount(count + 1)
  console.log(count)   // still the old value!
}

setCount doesn't change count in the running code. It asks React to re-render with the new value. The current function call keeps seeing the old one. The new value shows up on the next render.

So if you need the new value immediately, compute it into a local variable:

const next = count + 1
setCount(next)
saveToServer(next)

Rule 2: Use the function form when the new value depends on the old

setCount(count + 1)
setCount(count + 1)   // count is still the old value → only +1 total

Pass a function instead, and React gives you the latest value:

setCount(c => c + 1)
setCount(c => c + 1)   // +2 total ✅

Use this form whenever the update depends on the previous state — counters, toggles (setOpen(o => !o)), appending to lists — especially inside timers and async code where the variable may be stale.

Rule 3: Never mutate objects and arrays — replace them

React decides whether to re-render by checking if the value is a different object. If you change an object in place, it's the same object, so nothing happens:

user.name = 'Sam'
setUser(user)   // ❌ same object, no re-render

Create a new one with the spread operator:

setUser({ ...user, name: 'Sam' })   // ✅

Arrays work the same way. Use methods that return a new array:

Want to Do
Add setItems([...items, newItem])
Remove setItems(items.filter(i => i.id !== id))
Update one setItems(items.map(i => i.id === id ? { ...i, done: true } : i))

Avoid push, splice, sort and direct assignment on state arrays — they mutate. (toSorted() returns a new array.) (JavaScript array methods)

How much state do you need?

Less than you think. Don't store anything you can calculate:

const [items, setItems] = useState([])
const total = items.reduce((sum, i) => sum + i.price, 0)   // not state

Storing derived values in state means keeping two things in sync, which is where bugs come from.

When state lives in the wrong place

If two components need the same state, move it up to their closest shared parent and pass it down as props ("lifting state up"). When that gets awkward across many levels, look at context or a small state library. (React state management)

The summary

  • State is memory React keeps between renders; changing it triggers a re-render.
  • The variable doesn't change until the next render.
  • Use setX(prev => …) when the new value depends on the old.
  • Replace objects and arrays; never mutate them.
  • Don't store what you can compute.

EasySpawn gives Claude Code a persistent server with your app running, so it can trace a state bug in the live preview, fix it, and keep going while you're away. See how it works or join the waitlist.

Related: useEffect Explained · Too Many Re-renders · What Is React? · The Unique Key Prop Warning

Keep reading