Batching is when React groups multiple state updates into a single re-render for better performance. In React 17 and prior, updates inside React event handlers were batched. But updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default.
However, In React18 we are introduced to improved version of batching called Automatic Batching. It will enabled batching of all the state updates regardless of where they are called.
Let us break out this batching process React 17 and prior along with React 18 and understand the problems we faced earlier.
Problem with React 17 and Prior
Before looking into the issue, let us understand how batching works in React 17 and prior version. Our app contains two states to manage user name and city. onLoadUser function will be invoked on button click.
You can try the app in this @ StackBlitz . Click on a button and you will see console statement each time our App re-renders.
If you observe the browser console, you will see that state changed message is logged only once for both state updates. (PS: Initial log statement occurs when your app gets mounted for the very first time).
import React, { useState, useEffect } from 'react';
export default function App() {
const [name, setName] = useState();
const [city, setCity] = useState();
// Sync Function call
const onLoadUser = () => {
setName('Rahul');
setCity('Bangalore');
};
useEffect(() => {
console.log(`UseEffect Called: userDeatils ${name} ${city}`);
}, [name, city]);
return (
<div>
<h1>Print User details</h1>
<p>
User Name: <strong>{name}</strong>
</p>
<p>
City: <strong>{city}</strong>
</p>
<button onClick={onLoadUser}>Fetch user details</button>
</div>
);
}
Now you have seen that React batched both state updates and re-rendered the component only once.
But, what if we execute state updates in a context that is not associated with the browser?
For example, consider a fetch() call that asynchronously loads data:
If you observe the browser console after executing this example, you will see 2 messages. This indicates that two separate re-renders occur for each state update.
A fake API been used to fetch user details.
const onLoadUser = () => {
fetch('https://jsonplaceholder.typicode.com/users/1')
.then((res) => res.json())
.then((data) => {
setName(data.name);
setCity(data.address.city);
});
};
Any drawback here?
As observed. we see two different behaviors with synchronous and asynchronous functions . It is a performance bottleneck. If there are lot of states coming in and you are re-rendering fairly heavy there can be multiple re-renders.
That’s why React introduced Automatic Batching.
How Batching works in React18?
React v18 ensures that state updates invoked from any location will be batched by default. This will batch state updates, including native event handlers, asynchronous operations, timeouts, and intervals.
React version should be 18
If you observe the browser console after executing this example, you will see two messages. This indicates that only one re-render occurs as React batches all state updates regardless of where they are called.
Conclusion
To sum up that react 18 will batch the state updates for us no matter if it is in a simple function containing multiple state updates or a web API and interfaces like setTimeout, fetch or a promise containing multiple state updates.
To know more about Automatic batching, check out Discussions here
I write about the web; you can follow me on Twitter. If you liked the post, give some ❤️!! Cheers
Top comments (4)
React 18.2.0. It will not batch updating for state when using below code:
correct, only for code within the same Promise closure as shown in the docs: react.dev/blog/2022/03/29/react-v1...
Thanks for the article, RahulReddy!
I tried to add setTimeout in one of the state updates and if I am getting it right, theoretically React should batch the state updates and rerender just once (please see my code below). However it rerenders twice - can you advise why is this behaviour?
URL: stackblitz.com/edit/react-5eqlw5?f...
Thanks a mil in advance!
If both
set
are inside ofsetTimeout
it will batch as 1 render in React 18, but will be 2 renders in React 16.But in your example they will still be 2 renders in React 18 because they are being explicitly asserted to be separate since one is in
setTimeout
and one is not.You can see if you move
setCity
inside thesetTimeout
it will be 1 render in React 18. And you can see here in React 16 that it is 2: stackblitz.com/edit/react-13gs3c?f...All this is also more explicit in the official React docs: react.dev/blog/2022/03/29/react-v1...