useState

a value react remembers between renders. that it is a snapshot of the render that read it.

the value is frozen for that render

useState gives you two things: the value as it was when this render started, and a function that schedules the next render.

the first half:

<button onClick={() => setValue(value + 1)}>{value}</button>

and so does this one:

<button onClick={() => { setValue(value + 1); setValue(value + 1); }}>{value}</button>

both calls read the same value, the one captured when the component rendered. with value at 0, both schedule "set it to 1". the second call never sees the first.

the same thing bites anywhere that outlives the render: a setInterval, a promise callback, a listener registered once. they all keep reading the snapshot they closed over.

the updater form reads the current value

pass a function instead of a value and react hands you the state as it is at the moment the update is applied:

<button onClick={() => { setValue(v => v + 1); setValue(v => v + 1); }}>{value}</button>

now it adds 2.

if the next value depends on the previous one, use the function form. it costs nothing and removes a lot of headache.

coming from plain javascript

it is tempting to read useState as a variable with extra steps:

let value = 1;
btn.addEventListener('click', () => { btn.textContent = ++value; });

here value really is one box everyone reads and writes. in react it is not, every render gets its own.

when not to use it