Synchronous and Asynchronous State Updates and batch updates in React
React batches state updates to optimize performance, especially when multiple setState
calls are made within the same event loop. This means if you call setState
multiple times in a synchronous code block (like within an event handler), React will group those updates together and perform a single re-render.
Here's a detailed explanation with an example:
Example with Multiple setState
Calls
Let's say you have a component with two states, s1
and s2
:
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
s1: 0,
s2: 0
};
}
updateStates = () => {
this.setState({ s1: this.state.s1 + 1 });
this.setState({ s2: this.state.s2 + 1 });
};
render() {
console.log('Rendering...');
return (
<div>
<p>s1: {this.state.s1}</p>
<p>s2: {this.state.s2}</p>
<button onClick={this.updateStates}>Update States</button>
</div>
);
}
}
export default MyComponent;
How React Handles This
- Batching: When
updateStates
is called, bothsetState
calls are made synchronously. React batches these updates to optimize the rendering process. - Single Re-render: Instead of re-rendering twice (once for each
setState
call), React schedules a single re-render after all state updates are processed.
Why This Matters
Batching state updates is crucial for performance. It ensures that your component only re-renders once, even if multiple state changes occur. This reduces unnecessary renders and makes your application more efficient.
React's Update Mechanism
React batches state updates automatically in event handlers and lifecycle methods but not in asynchronous code like setTimeout
, promises, or async/await. For asynchronous updates, React 18 introduced automatic batching for updates, which helps handle this more efficiently.
Example with Asynchronous Code
updateStatesAsync = () => {
setTimeout(() => {
this.setState({ s1: this.state.s1 + 1 });
this.setState({ s2: this.state.s2 + 1 });
}, 1000);
};
In React 17 and earlier, the above asynchronous setState
calls would not be batched, leading to two separate re-renders. However, with React 18 and the introduction of automatic batching, even these updates are batched, resulting in a single re-render.
React 18 Automatic Batching Example
import React, { Component } from 'react';
import ReactDOM from 'react-dom/client';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
s1: 0,
s2: 0
};
}
updateStatesAsync = () => {
setTimeout(() => {
this.setState({ s1: this.state.s1 + 1 });
this.setState({ s2: this.state.s2 + 1 });
}, 1000);
};
render() {
console.log('Rendering...');
return (
<div>
<p>s1: {this.state.s1}</p>
<p>s2: {this.state.s2}</p>
<button onClick={this.updateStatesAsync}>Update States Asynchronously</button>
</div>
);
}
}
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<MyComponent />);