1 · Structure and rendering · 30 MIN
Events, state snapshots and updates
An event handler sees the state snapshot of its render.
Setting state requests another render; it does not change the variable inside the current handler. When the next value depends on the previous value, a functional updater lets React compose queued changes. The Add three button deliberately queues three updates to demonstrate this behavior. If each line used setCount(count + 1), all three would use the same old snapshot. Keep side effects out of updater functions because React may call them again while checking purity in development.
Click Add three twice and verify 6.
Read the example
import {useState} from 'react';
export default function App() {
const [count,setCount] = useState(0);
return <main><h1>Practice counter</h1><p aria-live="polite">{count} exercises</p>
<button onClick={()=>{setCount(n=>n+1);setCount(n=>n+1);setCount(n=>n+1)}}>Add three</button>
<button onClick={()=>setCount(0)}>Reset</button></main>;
}Check the expected output
Initially 0 exercises. Add three produces 3, then 6. Reset returns to 0.
Your challenge
Add a subtract button that cannot reduce the count below zero and use functional updates consistently.
Solution cost: O(1) state update per fixed set of actions. time · O(1) counter state. space
Common trap
A state variable is a snapshot, not a mutable reference to the newest value.
Study the project implementation
import {useState} from 'react';
export default function App() {
const [count,setCount] = useState(0);
return <main><h1>Practice counter</h1><p aria-live="polite">{count} exercises</p>
<button onClick={()=>{setCount(n=>n+1);setCount(n=>n+1);setCount(n=>n+1)}}>Add three</button>
<button onClick={()=>setCount(0)}>Reset</button></main>;
}Further reading: Official documentation
Next lesson: Stable keys and immutable list changes →