React 18 is the latest version of the React JavaScript library, and it comes with a variety of new features that aim to make the development experience smoother and more efficient. In this blog post, we’ll discuss the most noteworthy new features in React 18.
✅ Automatic Batching
What is batching ?
Let’s say that you are making a cake. If you were to optimize the trip to the grocery store, you would make a list of ingredients and go to the grocery store to get all the ingredients needed. This way you will have to go to grocery shop once.
This is batching. Without batching, you would start cooking, find out you need an ingredient, go to the grocery store and buy the ingredient, come back and continue cooking, only to find out you need another ingredient, go to the grocery store…and this goes on. This will drive you crazy. This is why batching is important. Now, React also follows this batch manner to update state and it improves performance.
🔴 Why React use batch manner to update state ?
In React, batching helps to reduce the number of re-renders that happen when a state changes, when you call setState
. Previously, React batched state updates in event handlers, for example:
const handleClick = () => {
setCounter();
setActive();
setValue();
};
//re-rendered once at the end
In the above example the component gets re-rendered 1 time even if even if there were 3 state updates and hence it is called batched state update and it improves performance. This batching was already there in React previous versions.
However, in React 17 state updates that happened outside of event handlers were not batched. For example, if you had a promise
or were making a network call
, the state updates would not be batched. Like this:
fetch("/network").then(() => {
setCounter(); //re-rendered 1 times
setActive(); //re-rendered 2 times
setValue(); //re-rendered 3 times
});
//Total 3 re-renders
Since here it is re-rendering the component 3 times hence it is not performant.
🔴 What new thing was introduced in React 18 ?
React 18 introduces automatic batching which allows all state updates – even within promises
, setTimeouts
, and event callbacks
– to be batched. This significantly reduces the work that React has to do in the background. React will wait for a micro-task to finish before re-rendering. Earlier in React 17 the batched state update was not available for promises, setTimeouts etc. But now from React 18 batching will be available for these as well. So now with the React v18 the above component will re-render 1 time only instead of 3 times.
Now for these automatic batching is available out of the box in React 18. But if you want to opt-out you can use flushSync
. So it means if you want some state update not to be part of the batch state updates then you can use the flushSync
function.
🎬 Watch the below video to see how we can use flushSync
function to opt out of batch state update.
What is React’s Automatic State Batching? (improved with React 18)
📝 Dan Abramov has written a detailed example in the below GitHub thread.
Automatic batching for fewer renders in React 18 · reactwg/react-18 · Discussion #21
✅ Transitions
Transitions can be used to mark UI updates that do not need urgent resources for updating.
For example, when typing in a typeahead field, there are two things happening: a blinking cursor that shows visual feedback of your content being typed, and a search functionality in the background that searches for the data that is typed.
Showing a visual feedback to the user is important and therefore urgent. Searching is not so urgent, and so can be marked as non-urgent.
These non-urgent updates are called transitions. By marking non-urgent UI updates as “transitions”, React will know which updates to prioritize. This makes it easier to optimize rendering and get rid of stale rendering.
You can mark updates as non-urgent by using startTransition
. Here is an example of what a typeahead component would like when marked with transitions:
import { startTransition } from "react";
// Urgent: Show what was typed
setInputValue(input);
// Mark any non-urgent state updates inside as transitions
startTransition(() => {
// Transition: Show the results
setSearchQuery(input);
});
✅ Suspense on the server
Server Component Suspense is a way to bring the benefits of the React Suspense API to server-rendered components. Suspense
on server is a new feature introduced in React 18 that allows you to use <Suspense>
and React.lazy
on the server.
Just like how Suspense
on the client-side allows you to defer rendering of a component until its dependencies are ready, Server Component Suspense allows you to defer the rendering of a server-rendered component until its dependencies are ready.
This can help improve the user experience by reducing the time it takes to load a page and making the page feel more responsive. It can also simplify the code required to handle data fetching and rendering on the server, since you can use Suspense to handle both in a unified way.
🔴 Why Suspense on the server is important ?
Server rendering further enhances the user experience of loading the page and reducing time to interactive.
Now what if most of your app is fast except for one part? Maybe this part loads data slowly, or maybe it needs to download a lot of JS before it gets interactive.
Before React 18, this part was often the bottleneck of the app, and would increase the time it took to render the component.
One slow component can slow down the entire page. This is because server rendering was all or nothing – you couldn’t tell React to defer loading of a slow component and couldn’t tell React to send HTML for other components.
React 18 adds support for Suspense on server. With the help of suspense, you can wrap a slow part of your app within the Suspense component, telling React to delay the loading of the slow component. This can also be used to specify a loading state that can be shown while it’s loading.
In React 18, one slow component doesn’t have to slow the render of your entire app. With Suspense, you can tell React to send HTML for other components first along with the HTML for the placeholder, like a loading spinner. Then when the slow component is ready and has fetched its data, the server renderer will pop in its HTML in the same stream.
Here is an example
import { Suspense } from "react/server";
function MyComponent() {
// ...
}
function App() {
return (
<div>
<h1>Hello, world!</h1>
<Suspense fallback={<div>Loading...</div>}>
<MyComponent />
</Suspense>
</div>
);
}
export default App;
So basically this new feature is Suspense
but for Server Side Rendered components.
Note: You don’t need to use React.lazy
with Suspense
if you are only using Suspense for data fetching. However, if you want to use Suspense
for code splitting
as well, then you need to use React.lazy
to import your components dynamically
✅ Concurrent Modes
Concurrent mode allows React to work on multiple UI updates at the same time without blocking the main thread. This means that React can start rendering a new UI while still showing the old one, and switch to the new one when it’s ready.
For example, let’s say you have a button that triggers a data fetch and a UI update. In synchronous mode (the default mode in React), clicking the button would block the UI until the data is fetched and the new UI is rendered. This can make the app feel slow and unresponsive.
In concurrent mode, clicking the button would start fetching the data and rendering the new UI in parallel, without blocking the current UI. React would use a feature called startTransition to indicate that this update is not urgent and can be delayed if necessary. This way, React can prioritize user input events over rendering tasks and keep the app responsive.
Concurrent Mode is a set of new features that help React apps stay responsive and gracefully adjust to the users device capabilities and network speed.
✅ New Root API
The new root API in React 18 is ReactDOM.createRoot
. This is a new method to create a root for rendering or unmounting your React components. The new root API replaces the old ReactDOM.render
method, which is still supported but does not enable the new concurrent features of React.
The new root API has some advantages over the old one:
- It provides better ergonomics for managing roots. You don’t need to pass the container element every time you want to update your component. Instead, you can store the root object and call render on it with just the component.
- It enables concurrent rendering, which allows React to update your user interface without blocking the main thread. Concurrent rendering also enables features like automatic batching, transitions, suspense and streaming server-side rendering.
To use the new root API, you need to install React 18 alpha version and replace ReactDOM.render
with ReactDOM.createRoot
in your code. For example:
// Old way
import ReactDOM from "react-dom";
import App from "./App";
const container = document.getElementById("root");
ReactDOM.render(<App />, container);
// New way
import { createRoot } from "react-dom";
import App from "./App";
const container = document.getElementById("root");
const root = createRoot(container);
root.render(<App />);
✅ Strict mode in React 18
Strict mode in React 18 is a feature that helps identify and highlight potential issues in your code, making it easier to catch and fix bugs. When you enable strict mode, React will perform additional checks and validations, such as warning you when you use legacy lifecycle methods or when you access state directly. This can help you write more robust and maintainable code, and ensure that your applications are reliable and performant.
Final Thoughts
React 18 is shaping up to be a significant release for the popular JavaScript library, with several new features that promise to make building complex web applications faster and more efficient than ever before. With the addition of concurrent rendering, automatic batching of state updates, server components, and improved accessibility,
React 18 has something for everyone. If you’re a React developer, it’s worth getting familiar with these new features and incorporating them into your projects.