State Batching
Overview
If you call three different state setters (setName, setAge, setRole) inside a single event handler, does React re-render the component three separate times?
No. React is highly optimized. It uses a technique called State Batching. When an event handler fires, React waits until the entire function finishes executing. It gathers all the requested state updates, batches them together, and triggers only one single re-render at the very end. This prevents terrible performance issues and visual flickering.
However, this optimization introduces a major pitfall: Stale State. Because state updates are queued (not applied instantly), if you try to update the exact same state variable multiple times in a row based on its previous value, React will use the old, outdated value for all calculations.
Syntax
function Counter() {
const [count, setCount] = useState(0);
const badIncrement = () => {
// If count is 0, React queues: setCount(0 + 1)
setCount(count + 1);
// count is STILL 0 here! React queues: setCount(0 + 1) again!
setCount(count + 1);
// Result: The count only goes up by 1, not 2.
};
const goodIncrement = () => {
// By passing a CALLBACK FUNCTION instead of a raw value,
// React guarantees 'prev' is the most up-to-date value in the queue.
setCount(prev => prev + 1);
setCount(prev => prev + 1);
// Result: The count successfully goes up by 2.
};
}Common Pitfalls
- Logging state immediately after setting it: If you call
setCount(5)and writeconsole.log(count)on the very next line, it will print the old value (e.g., 0). State variables behave like constants during a single render cycle. The new value of 5 will only be available on the next render.
Interview Questions
Prior to React 18, React only batched state updates inside React event handlers (like onClick). Updates inside setTimeout or Promises caused multiple re-renders. React 18 introduced Automatic Batching, which universally batches ALL state updates regardless of where they happen, significantly improving performance.
You MUST pass a callback function (an Updater Function) anytime your new state mathematically depends on the previous state (e.g., counters, toggles). This guarantees you are calculating against the latest queued value, avoiding stale closures.
Real-World Example
Batching Form Submission States: In a complex form, we update multiple UI states simultaneously. Without batching, the UI would re-render for every single set call, potentially causing janky animations and massive performance drops.
function Checkout() {
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState(null);
const [success, setSuccess] = useState(false);
const handleSubmit = async () => {
// React batches these two updates. The UI only re-renders ONCE.
setIsSubmitting(true);
setError(null);
try {
await processPayment();
// React batches these two updates. The UI only re-renders ONCE.
setSuccess(true);
setIsSubmitting(false);
} catch (err) {
setError(err.message);
setIsSubmitting(false);
}
};
}Check Your Knowledge
Test your understanding of State Batching with these quick questions.