July 9, 2021
Earlier versions of React batched multiple state updates only inside React event
handlers like click or change to avoid multiple re-renders and improve
performance.
React 18 adds automatic batching for all use cases to improve performance even further. Now, React batches state updates in React events handlers, promises, setTimeout, native event handlers and so on.
Let's jump into an example to understand different use cases of batching.
In these examples we are assuming that you already replaced render with
createRoot API. Automatic batching only works with createRoot API. Please
check this discussion to
learn more about replacing render with createRoot.
We're using simplest example, also used in the original discussion of this change.
const App = () => {
  const [count, setCount] = useState(0);
  const [flag, setFlag] = useState(false);
  const handleClick = () => {
    setCount(count + 1);
    setFlag(!flag);
  };
  return (
    <div>
      <button onClick={handleClick}>Click here!</button>
      <h1>{count}</h1>
      <h1>{`${flag}`}</h1>
    </div>
  );
};
Note - React automatically batched all state updates inside event handlers even in previous versions.
const handleClick = () => {
  fetch("URL").then(() => {
    setCount(count + 1);
    setFlag(!flag);
  });
};
setTimeoutconst handleClick = () => {
  setTimeout(() => {
    setCount(count + 1);
    setFlag(!flag);
  }, 1000);
};
const el = document.getElementById("button");
el.addEventListener("click", () => {
  setCount(count + 1);
  setFlag(!flag);
});
In each of the above case, both state update calls will be batched by React and performed together at once. This avoids re-rendering component with partially updated state in any event.
In some cases, where we do not wish to batch state updates, we can use
flushSync API from react-dom. This is mostly useful when one updated state
is required before updating another state.
import { flushSync } from "react-dom";
const handleClick = () => {
  flushSync(() => {
    setCounter(count + 1);
  });
  flushSync(() => {
    setFlag(!flag);
  });
};
Using flushSync can be used to fix breaking changes when upgrading to
React 18. There are chances that in earlier versions of React we might be using
one updated state before updating other states. Simple solution is to wrap all
such state updates inside individual flushSync API.
For more detailed information on automatic batching, head to this discussion.
If this blog was helpful, check out our full blog archive.