React Batching Updates and flushSync

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

React Batching Updates and flushSync

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

React Automatic Batching

React tries to automatically batches multiple state updates when rendering to provide high performance user experience.

This is demonstrated in the below example, where multiple state changes result in a single re-render. Note that it happens even if the updates are done outside of a callback controlled by React (e.g. setTimeout).

App.tsx
import { useState } from "react";

export default function App() {
const [count, setCount] = useState(0);
const [toggle, setToggle] = useState(false);

const handleClick = () => {
setTimeout(() => {
// Note that these two state changes only result in single re-render
setCount((count) => count + 1);
setToggle((toggle) => !toggle);
}, 500);
};

console.log("Rendered", count, toggle);

return (
<div>
<button onClick={handleClick}>Click Me</button>
<div>Count: {count}</div>
<div>Toggle: {toggle.toString()}</div>
</div>
);
}

Opting out of Automatic Batching

Sometimes you need to opt out of automatic batch re-rendering to do an immediate re-render. React provides a utility flushSync for this purpose.

An example use case is re-rendering the application synchronously before React hands over the current page to the browser for printing.

App.tsx
import { useState, useEffect } from 'react';
import { flushSync } from 'react-dom';

export default function PrintApp() {
const [isPrinting, setIsPrinting] = useState(false);

useEffect(() => {
function handleBeforePrint() {
console.log('isPrinting true');
flushSync(() => {
setIsPrinting(true);
});
}
function handleAfterPrint() {
console.log('isPrinting false');
setIsPrinting(false);
}

window.addEventListener('beforeprint', handleBeforePrint);
window.addEventListener('afterprint', handleAfterPrint);
return () => {
window.removeEventListener('beforeprint', handleBeforePrint);
window.removeEventListener('afterprint', handleAfterPrint);
}
}, []);

return (<>
<div>isPrinting: {isPrinting ? 'true' : 'false'}</div>
<button onClick={() => window.print()}>Print</button>
</>);
}
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

A little known feature within React is that it automatically batches multiple stage changes into a single Tom update. This is great for performance. So in this lesson we will demonstrate this automatic batching within React. We will also look at how we can opt out of it if we want to and even cover an example where we actually need to. So let's go consider a simple React application where we have two state variables. One, to manage a simple count of type number and a simple toggle of type Boolean. We have a utility function that modifies both of these state variables.

00:33

It increments the count by one and then inverts the toggle that is two to false or false to two. Now every time the component is going to get rendered, we are going to log to the console the current value of the count and the current value of the toggle. This allows us to observe how many times the component gets rendered within the ui. We have a simple button wide that handles click itty function that we saw, and then a simple display for the count and the toggle. If you run the application, you can see the simple button, you can see the count and you can see the current

01:05

value of the toggle. And additionally, on the console you can see that the application has been rendered only once. Now, when we invoke handle click by clicking the button, we make two state calls, one to modify the count and one to modify the toggle. However, as you can see on the console, the application only renders once after both of these changes have been committed. And this is the basics of the automatic patching that React performs. We can make as many state modifications as we want in a synchronous call and only after that synchronous call completes does React re-render

01:39

the component with the new state values. And this automatic batching actually even happens if the call is triggered outside of React. To demonstrate that, let's wrap up calls within a set timeout so that React is not responsible for scheduling these calls. And now when you click the button, the call will actually happen after half a second, but you can see that the component still only renders once for each of these two state changes. This is great because it saves us from pointlessly re rendering the component for each call to a state change and only renders the component

02:12

after all the state changes have been made. If you don't quote automatic patching of multiple state changes, you can wrap individual calls or a group of calls in a method called flashy to immediately render a component After a state change. This utility function is provided by the React team and we can import it from the React dom module. Now instead of calling set count and set toggle in the same function, we can make the set count call in a callback that we provide to flush sync. As soon as the callback that we provide to flush sync exits, we will see a re-render of the application.

02:46

And this will be of course with the updated value of the count. And at a later point because of that set toggle call, there will be a reader of the application with a modified toggle value as well. So we should see Two renders, one that happens immediately and synchronously as soon as flush sink exits and one at a later point because of that set toggle. So the flush sink function allows us to forcefully immediately read under the application after some state changes. As you would imagine most of the time you would want this automatic delayed batching

03:18

to get the best performance, but you might want to use first think for forcefully and immediately regenerating the UI in certain scenarios. So let's take a look at an example. We have a simple application simplified a bit from the React documentation where we have a bullion for a printing, which we initialized to false utilizing use effect on component mount. We will subscribe to the browser before print to set printing to True and after print to set the printing bullion to false. For registering these listeners, we subscribe using Window Adamant listener

03:51

and of course on unmount we will make sure that we remove these listeners as well. Our UI will be extremely simple. It consists of a simple div displaying the is printing Boolean, and then a simple button to invoke window print. Here's what should happen When the user clicks the print button, we invoke window print which triggers the browser to emit the event before print, which we handle in handle before print. We log to the console that we are going to set is printing to true and of course that is what we immediately do. And then the browser should display the print dialogue.

04:26

Think of this is printing Boolean as some state modifications and re rendering of the application that you might want to do to put the application into a view that is more appropriate for printing. In our particular case, we just wanted displaying is printing true. Now, when we run this application and actually try to do this thing, after clicking the print button, you can see that on the console it does log that we have tried to read under the application with is printing True. However, the view still displays is printing false. And here's the reason why.

04:58

Because of reacts automatic patching, the updates happen asynchronously, but the print dialogue is displayed before the state gets updated and the component gets red rendered. So the solution to this problem is to simply wrap our is printing bullion change with a call to flush sync. And now after the state changes, the component will read under synchronously before the browser displays the print dialogue. And of course we can verify that this is the correct change by running the application and clicking the print button. You can see that the bullion is updated on the console as well as the component has been rended with the new value.

05:34

I'll wrap things up there. As always, thank you for joining me and I'll see you in the next one.