Questions
This button should increment twice, but only increments once. How do you fix it?
A developer has a Counter component with a “Double Increment” button. The intention is that when this button is clicked, the counter’s value should increase by 2.
They’ve implemented this by calling setCount(count + 1) twice in the same event handler. However, they notice a bug: clicking the “Double Increment” button only increments the count by 1, not by 2.
You’ve been given the Counter component.
- Explain why calling
setCount(count + 1)twice in a row only increments the count by 1. - Fix the component to correctly increment the counter by 2 with a single button click.
import React, { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const handleDoubleIncrement = () => {
// THE BUG: Calling setCount with a direct value multiple times
// in the same event handler.
setCount(count + 1); // React sees count as 0, schedules update to 1
setCount(count + 1); // React still sees count as 0, schedules update to 1 again
};
return (
<div style={{ padding: '20px' }}>
<h1>Count: {count}</h1>
<button onClick={handleDoubleIncrement}>Double Increment</button>
<p>Click the button and observe the count.</p>
</div>
);
} Calling `setCount(count + 1)` twice uses the same stale count value from the current render closure. Both calls schedule updates to the same value, and React batches them, resulting in only one increment.
Use the functional update form `setCount(prevCount => prevCount + 1)`. Each call receives the most recent state value as an argument, ensuring sequential updates are applied correctly even when batched.
The bug occurs because React’s state updates (triggered by setCount) are asynchronous and often batched for performance reasons.
When handleDoubleIncrement is called:
setCount(count + 1): At this point,countis0. React schedules an update to setcountto1.setCount(count + 1): Immediately after, this line is executed. Crucially,countin this line still refers to the value from the current render scope, which is0. So, React schedules another update to setcountto1.
Because React batches these updates, it sees two requests to set count to 1. The second request effectively overwrites the first, resulting in the count only increasing by 1.
To ensure that state updates are based on the most recent state, you should use the functional update form of setCount (or setState in class components). This form passes a function to setCount, and that function receives the prevState as its argument. React guarantees that prevState will always be the latest available state.
Here is the corrected implementation:
import React, { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
const handleDoubleIncrement = () => {
// FIX: Use the functional update form to ensure each update
// is based on the previous state.
setCount(prevCount => prevCount + 1); // Schedules update based on prevCount
setCount(prevCount => prevCount + 1); // Schedules another update based on the result of the first
};
return (
<div style={{ padding: '20px' }}>
<h1>Count: {count}</h1>
<button onClick={handleDoubleIncrement}>Double Increment</button>
<p>Click the button and observe the count.</p>
</div>
);
}By using setCount(prevCount => prevCount + 1), each call to setCount now correctly receives the result of the previous state update, ensuring that the counter increments by 2.
Practice Question
When should you use the functional update form of `setCount` (e.g., `setCount(prevCount => prevCount + 1)`)?