What’s new in React v18.0?

Rahul
8 min readApr 15, 2022

--

Some exciting new improvements have been launched with React v18.0

When React 18 was announced a year ago, the team promised a gradual adoption strategy. Now, a year later, this is exactly what they’ve done and you can upgrade your app to the newest version.

Updating from React 17 to 18 only requires a simple step:

1) Install the latest version:

npm install react@18 react-dom@18

Let’s dive into the React 18.0’s new features and updates.

1. The Legacy Root API vs. The New Root API

React 18 ships two root APIs, which we call the Legacy Root API and the New Root API.

  • Legacy root API: This is the existing API called with ReactDOM.render. This creates a root running in “legacy” mode, which works exactly the same as React 17. Before release, the React team will add a warning to this API indicating that it’s deprecated and to switch to the New Root API.
  • New root API: The new Root API is called with ReactDOM.createRoot. This creates a root running in React 18, which adds all of the improvements of React 18 and allows you to use concurrent features. This will be the root API moving forward.

Usage:

//React 17import * as ReactDOM from "react-dom"
import App from "App"

// The <App/> component is directly attached to a DOM element with the id of 'app':
ReactDOM.render(<App />, document.getElementById("app"))

The New Root API uses ReactDOM.createRoot(), creates a new root element, and React app is rendered in it:

//React 18import * as ReactDOM from "react-dom"
import App from "App"
// Create a root by using ReactDOM.createRoot():
const root = ReactDOM.createRoot(document.getElementById("app"))
// Render the main <App/> element to the root:
root.render(<App/>)

Hydrating in Legacy Root API vs. Hydrating in the new Root API

import ReactDOM from 'react-dom'
import App from 'App'
//React 17const container = document.getElementById('app');
ReactDOM.hydrate(<App />, container)
//React 18const container = document.getElementById('root');
const root = ReactDOM.createRoot(container, { hydrate: true });
root.render(<App />);

2. Concurrent features

In React 18 with concurrent rendering, React can interrupt, pause, resume, or abandon a render. This allows React to respond to the user interaction quickly even if it is in the middle of a heavy rendering task.

Before React 18, rendering was a single, uninterrupted, synchronous transaction and once rendering started, it couldn’t be interrupted.

Concurrency is a foundational update to React’s rendering mechanism. Concurrency allows React to interrupt rendering.

React 18 introduces the foundation of concurrent rendering and new features such as suspense, streaming server rendering, and transitions are powered by concurrent rendering.

React 18 will be the first React release adding opt-in support for concurrent features such as:

  • startTransition: lets you keep the UI responsive during an expensive state transition.

With the new update, each state update is classified as one of these two categories: it is either an urgent update or a transition update (transition, for short). Urgent updates are actions which the user intuitively expects to respond in a heartbeat, like a mouse click or a keypress. The transition updates are actions that a little delay is acceptable and at many times expected, like a search query. The startTransitionAPI marks the setState calls inside it as transitions , meaning they are interruptable. Transition updates also run synchronously, but the UI is not blocked when they are running.

import { startTransition } from "react"// Urgent update that shows whatever is typed by the user:
setInputValue(inputData);
// Transition updates are marked with startTransition:
startTransition(() => {
// A non-urgent, interruptable update of the search query:
setSearchQuery(inputData);
})

If the setSearchQuery(input)wasn't marked as a transition update, the UI would be locked after each input change. Now that it is marked as non-urgent, the user can search for something and change opinions and decide to navigate to another page before the UI is updated according to the input change, and doesn't have to wait for a UI update that is not of interest.

You can even track the pending state of a transition update and show the user a loading UI if you want to, using the useTransitionhook:

import { useTransition } from "react"
const [isPending, startTransition] = useTransition()
// For example, you can show a loading spinner when it's pending:
{
isPending ? <LoadingSpinner /> : null
}
  • The useDeferredValuehook helps you defer updating some part of the UI by a specified time period while keeping the page responsive. You can also give it an optional timeout. React will try to update the deferred value as soon as it can. If it fails to do so within the given timeout period, it will then force the update, blocking the UI in the process. In other words, the deferred value is updated via a transition update rather than an urgent update, keeping your UI responsive in the process.
import { useDeferredValue } from "react"const deferredValue = useDeferredValue(value, {
timeoutMs: 3000,
})
  • useId : we can use this new hook to create a new identifier in the server and in the client.
function Checkbox() {
const id = useId();
return (
<>
<label htmlFor={id}>Do you follow This Dot on Twitter?</label>
<input id={id} type="checkbox" name="react"/>
</>
);
};

3. Automatic batching

React only re-renders the UI once. This means React batches (or groups) all the changes together, so instead of doing one state update and re-rendering the UI and doing the following state update and re-rendering the UI again, it does both of the state updates and re-renders the UI just once.

Batching is a great mechanism that protects us from unnecessary UI re-renders, but React 17 only did it in event handlers. The usage of promise chains, asynchronous code, or event handlers broke this behavior. With React 18, batching is done automatically in native event handlers, promises, and asynchronous code.

function handleClickEvent() {
// React 17: Re-rendering happens after both of the states are updated
// This is also done in React 18 by default.
setIsLoggedIn(loggedIn => !loggedIn);
setCount(count => count+ 1);
}
// For the following code blocks, React 18 does automatic batching, but React 17 doesn't.
// 1. Promises:
function handleClickEvent() {
fetchData().then(() => {
setIsLoggedIn(loggedIn => !loggedIn)
setCount(count => count+ 1)
})
}
// 2. Asynchronous code:
setInterval(() => {
setIsLoggedIn(loggedIn => !loggedIn)
setCount(count => count+ 1)
}, 3000)
// 3. Native event handlers:
element.addEventListener("click", () => {
setIsLoggedIn(loggedIn => !loggedIn)
setCount(count => count+ 1)
})

4. New Suspense SSR and Selective Hydration

  • Server-side rendering, also known as SSR, is a way of rendering web pages that let you generate HTML from React components directly on the server and share the HTML with users. Users can see a preview of the page through SSR even before the JavaScript bundle presents loads and runs. But sometimes, the JavaScript on the backend takes a long time to get processed, and the time taken is known as Hydrating Time (The process of rendering React components and adding event handlers to the server-side rendered HTML document is referred to as hydration).
  • React 18 will include architectural improvements to the React SSR’s performance. The new update will allow Streaming HTML directly on the server, i.e., the server sends pieces of components as they get rendered using another component known as Suspense, which decides which parts of the application might take longer to load and what shall be rendered directly. Using a selective hydration method, components that are wrapped with Suspense will not block hydration anymore. Every ready component will start hydrating once the browser gets both its content and JavaScript code.

<SuspenseList> </SuspenseList>

<SuspenseList> lets you coordinate the appearing order of the content of Suspense nodes of the subtree it wraps, even if the data arrives in a different order. Normally, if you have multiple sibling Suspense boundaries, they will resolve whenever they can. However, you might want to load your components in a particular order, no matter in which order they resolve themselves.

import { Suspense, SuspenseList } from "react";<SuspenseList revealOrder="forwards">
<Suspense fallback="Loading first item...">
<FirstItem />
</Suspense>
<Suspense fallback="Loading second item...">
<SecondItem />
</Suspense>
<Suspense fallback="Loading third item...">
<ThirdItem />
</Suspense>
</SuspenseList>

In the example above, even if the third item is loaded first, it will render Loading third item..., until the first item is loaded. When the first item is loaded, the first item is rendered, along with the fallback for the second and third. Only when the second item is loaded all three can be rendered.

The revealOrderprop can take the values forwards, backwards, and together. The forwardsand backwardsprops allow Suspense boundaries inside to resolve in the forwards and backwards order. togetheron the other hand waits for all boundaries to resolve before rendering all of them.

You can also give the SuspenseList a tailprop. The tailprop can take collapsedand hiddenvalues. By default, SuspenseList renders all fallbacks. However, if you want to render no fallbacks, you can use the tail="hidden"prop, and if you only want to render at most one fallback, you can use tail="collapsed". This way, you can create many fallbacks without worrying about cluttering your loading area.

5. Strict mode

Strict mode in React 18 will simulate mounting, unmounting, and re-mounting the component with a previous state. This sets the ground for reusable state in the future where React can immediately mount a previous screen by remounting trees using the same component state before unmounting.

Strict mode will ensure components are resilient to effects being mounted and unmounted multiple times.

6. Components Can Now Render undefined

React no longer throws an error if you return undefined from a component. The allowed component returns values consistent with allowed values in the middle of a component tree. The React team suggests using a linter to prevent mistakes like forgetting a return statement before JSX.

7. No setState Warning on Unmounted Components

Previously, React warned about memory leaks when you called setState on an unmounted component. This warning was added for subscriptions, but people primarily ran into it in scenarios where the setting state was fine, and workarounds would worsen the code.

--

--

Rahul

Full stack developer | MERN | MEVN | Blockchain