Introduction
React automatically batches multiple state updates inside event handlers to improve performance. However, in some cases, you may want to prevent automatic batching and force React to update the state synchronously. This tutorial explains automatic batching, why it happens, and how to disable it using flushSync
.
What is Automatic Batching?
Automatic batching means that multiple state updates within an event handler are grouped together into a single render cycle, reducing unnecessary re-renders.
Example of Automatic Batching
import React, { useState } from "react"; const App = () => { const [count, setCount] = useState(0); const [text, setText] = useState(""); const handleClick = () => { setCount((prev) => prev + 1); setText("Updated!"); console.log("Count after update:", count); // Might log the old count due to batching }; return ( <div> <p>Count: {count}</p> <p>Text: {text}</p> <button onClick={handleClick}>Update</button> </div> ); }; export default App;
Expected Behavior
- React batches the
setCount
andsetText
updates into a single render. - The
console.log("Count after update:", count);
might log stale values because state updates are applied asynchronously.
How to Prevent Automatic Batching?
If you want state updates to apply immediately, you can use flushSync
from react-dom
.
Example Using flushSync
import React, { useState } from "react"; import { flushSync } from "react-dom"; const App = () => { const [count, setCount] = useState(0); const [text, setText] = useState(""); const handleClick = () => { flushSync(() => setCount((prev) => prev + 1)); // Forces an immediate update console.log("Count after first update:", count); // Logs updated value flushSync(() => setText("Updated!")); console.log("Text after second update:", text); }; return ( <div> <p>Count: {count}</p> <p>Text: {text}</p> <button onClick={handleClick}>Update</button> </div> ); }; export default App;
Behavior with flushSync
flushSync
forces React to processsetState
updates immediately.- Each
flushSync
call triggers a separate re-render before moving to the next state update. - The console logs will now display the correct updated values.
Behavior with flushSync
flushSync
forces React to processsetState
updates immediately.- Each
flushSync
call triggers a separate re-render before moving to the next state update. - The console logs will now display the correct updated values.
When Does React NOT Batch Updates?
There are certain situations where React automatically disables batching:
1. Inside setTimeout
or setInterval
Updates inside asynchronous callbacks are not batched, so each update causes a separate re-render.
setTimeout(() => { setCount(count + 1); setText("Updated!"); }, 1000);
Each state update happens independently in separate render cycles.
2. Inside Native Event Listeners (e.g., addEventListener
)
If you use a DOM event listener, React does not batch the updates.
document.getElementById("myButton").addEventListener("click", () => { setCount(count + 1); setText("Updated!"); });
Since React is not controlling this event, state updates happen separately.
When Should You Prevent Batching?
If you need state updates immediately, such as:
- Logging the latest state inside an event handler.
- Measuring DOM elements before and after a state update.
- If you need precise control over re-renders.
- When working with third-party libraries that expect immediate state changes.
Conclusion
- React batches state updates automatically to optimize performance.
- Use
flushSync
to force immediate updates when needed. - React does not batch updates in
setTimeout
,setInterval
, or native event listeners. - Preventing batching is useful for debugging, animations, and measuring DOM updates.