What are the new changes in React 18?

What are the new changes in React 18?

Published 8th, November, 2022

4 min read

The last decade focused on browser technologies that don’t hamper user experience and web performance. React 18 ships out-of-the-box features focused on performance improvements and concurrency.

Table of Content:

  1. Gradual adoption path for Concurrent React
  2. Automatic Batching on React 18
  3. Introducing Transitions
  4. Suspense Features
  5. Client and Server Rendering APIs
  6. Strict Mode Behaviours
  7. Hooks Updates in React18
  8. Final thoughts & anticipated changes

Gradual adoption path for Concurrent React

Concurrent React unlocks features that let developers keep many versions of the UI. At the same time, the mechanism details are under the hood. The public APIs only expose features for the devs to focus on the “what” and “how” of a UI.

Concurrent React has evolved to improve performance by scheduling high and low-priority tasks. React v17 allows running different versions of React on the same app. This way, some parts of the application could upgrade to React v18.

Upgrading to React 18 before adding concurrent features, the render updates synchronously as in the previous version.

To implement concurrent features, import createRoot.

import { createRoot } from "react-dom/client";

Enabling v18 in the parts of your application, the components behave slightly differently.

The concurrent renderer evaluates the DOM tree first and prepares new screens in the background. It doesn’t interrupt the main thread. It can pause an update, come back to it later, or even abandon it.

The implementation responds to user inputs in the middle of ongoing rendering tasks. The React team encourages you to use concurrent features at your own pace.

Automatic Batching on React 18

This feature pertains to state management. In React 17 and earlier versions, changing a component’s state multiple times via user events batched the updates. It made one re-render, avoiding unnecessary re-rendering. Though it works for some DOM events, the behavior isn’t consistent for other activities like data fetching.

With automatic batching, setting states after actions like data fetch, React batches them and makes one re-render. React v18 makes automatic batching a default feature. It is an upgrade from the previous version that made multiple re-renders on the same action.

Introducing Transitions

The concept of transitions is new and separates urgent updates and transition updates.

  • Urgent updates: App interactions that cannot afford delays like clicks, press, or scrolling.
  • Transition updates: Transitioning UI from one view to another with minuscule delay.

The startTransition API defines both urgent and transition updates inside an input event.

import { startTransition } from 'react';

Urgent updates are the same as setting states. User inputs via setInputValue(input) are high-priority and hence an urgent update. Rendering under startTransition() are interruptible for urgent changes.

useTransition() hook is for non-urgent updates and delays the rendering. The hook returns a boolean - pending to state if a transition is active or not.

For the best user experience, single user input results in both urgent and non-urgent. Transitions being interruptible operate on the concurrent rendering shipped in this version.

Suspense Features

Suspense features, as the name suggests, suspend rendering a component till it’s ready. Introduced in React 16.6, suspense features come in handy in lazy loading and data fetching.

React 18 has added support for server-side rendering. It uses transitions to avoid hiding existing content. You can specify a loading state with a fallback if the component is not ready

A sample where ProfileName returns a name and ProfTimeline has a list of data points fetched.

function ReadUserInfo() {
 return (
   <Suspense fallback={<h1>Loading profile...</h1>}>
     <ProfileName />
     <Suspense fallback={<h1>Loading Details...</h1>}>
       <ProfTimeline />

Client and Server Rendering APIs

As React v18 implements concurrent React, it encourages adoption at your pace. It allows using old APIs while upgrading to newer ones.

From React DOM client, there are two new APIs:

  • createRoot: a new method in place of ReactDOM.render to create a root element or unmount. New features won’t work without it.
  • hydrateRoot: replaces ReactDOM.hydrate for filling an empty object with data via database or external sources. Work in conjunction with ReactDOM Server APIs.

The new APIs use onRecoverableError for error handling. It notifies when React recovers during rendering or hydrating.

For React DOM server, the updates are for supporting streaming APIs:

  • renderToPipeableStream: only available for Node.JS environment.
  • renderToReadableStream: for modern edge environments similar like CloudFlare.

Strict Mode Behaviours

This version of React is only for development-only environments which automatically mounts and remounts a component on the first mount. It restores the previous state on the second mount.

The update is not extensive, but React team points to the ease of adding and removing UI sections while preserving states.

Hooks Updates in React18

React apps carry a lot of functionalities via hooks and avoid creating custom classes. This version adds new hooks, one we have already encountered in Transitions. Here’s a list of new additions:

  • useID: Browsers use APIs like aria and ally, and they rely on ids to link components. Earlier, ids were randomly generated that broke during client-side mounting. UseOpaqueIdentifier provided temporary relief but with many limitations and bugs. useID generates stable ids for apps executing on both the server and client sides. It avoids component hydration mismatches. It makes state management and execution easier.
  • setTransition: Transition aims to segregate urgent from non-urgent updates to work smoothly with interruptible threads. With this implementation, React allows state updates that feel more intuitive to the user.
  • useDeferredValues: It aims for an intuitive user experience where a user expects a quick response like input display vs a reasonably delayed response like image loading. useDeferredValue allows intentional delays where it’s expected and renders when the new state results are ready. It doesn’t interrupt high-priority tasks like user input. It works similarly to debouncing but better.
  • useSyncExternalStore: Intended to use in libraries, this hook is not for application codes. Since React v18 interrupts thread, it leads to UI consistency issues like tearing on data fetch. React team added useMutableSource hook earlier which turned out difficult for library maintainers.
  • useSynExternalStore: allows proper subscriptions to external data sources and removes the dependency on useEffect().
  • useInsertionEffect: Also intended for library maintenance, it addresses performance issues of CSS-in-JS libraries when styles are injected in the render. The hook runs once DOM is mutated.

Note: Though React v18 introduces significant updates, it also changes how you would expect a component to behave. Since a component mounts, unmounts, and remounts in strict mode, you run into useEffect() twice in development mode.

If you are fetching data, disable strict mode to avoid fetching it twice. This behaviour doesn’t occur in production.

Final thoughts & anticipated changes

Concurrent React is at the heart of this version. The updates prioritize tasks and seamless user experience. Thread interruptions make it possible without hampering performance. In the future, React team plans to add server components for improved performance, easier access to data with Suspense, and additional tools under concurrent rendering.