Learnitweb

Preventing Automatic Batching in React

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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
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;
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 and setText 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

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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;
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;
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 process setState 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 process setState 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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setTimeout(() => {
setCount(count + 1);
setText("Updated!");
}, 1000);
setTimeout(() => { setCount(count + 1); setText("Updated!"); }, 1000);
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.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
document.getElementById("myButton").addEventListener("click", () => {
setCount(count + 1);
setText("Updated!");
});
document.getElementById("myButton").addEventListener("click", () => { setCount(count + 1); setText("Updated!"); });
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.