5 min read
0%

Automatic Batching (React 18)

Back to Blog
Automatic Batching (React 18)

Automatic Batching (React 18)

Batching is React grouping multiple state updates into a single re-render. React 18 extended batching to cover all update origins — not just React event handlers.

React 17 and Earlier

In React 17, batching only applied inside React-controlled event handlers:

function handleClick() {
  setCount(c => c + 1); // no render yet
  setFlag(f => !f);     // no render yet
  // render happens once here
}

But updates triggered outside React’s event system rendered immediately:

setTimeout(() => {
  setCount(c => c + 1); // renders immediately
  setFlag(f => !f);     // renders again
}, 1000);

Promises and native event listeners had the same problem — two state updates meant two renders.

React 18: Automatic Batching Everywhere

With createRoot, React 18 batches all updates regardless of origin:

// setTimeout
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // single render
}, 1000);

// fetch callback
fetch('/api').then(() => {
  setData(result);
  setLoading(false);
  // single render
});

// native event
element.addEventListener('click', () => {
  setA(1);
  setB(2);
  // single render
});

How It Works

React 18 schedules all updates through its scheduler. Even updates from setTimeout go through a microtask-based batching flush rather than synchronous dispatch. The scheduler collects updates, then flushes them together at the next opportunity.

Opting Out: flushSync

If you need a state update to render immediately before continuing:

import { flushSync } from 'react-dom';

flushSync(() => {
  setCount(c => c + 1);
});
// DOM is updated here
flushSync(() => {
  setFlag(f => !f);
});
// DOM is updated here too

Use flushSync sparingly — it defeats batching and is mainly needed when interoperating with non-React code that reads the DOM directly after a state update.

Legacy Root Behavior

Code using ReactDOM.render() (legacy root) retains React 17 behavior. Automatic batching only applies to roots created with createRoot.


Canvas is not supported in your browser