We want to hear from you!Take our 2021 Community Survey!

React v18.0 Release Candidate: Upgrade Guide

February 25, 2022 by Rick Hanlon

Today we’re publishing the second release candidate for React 18. With this release, we’re encouraging users to install and test React 18 to raise any issues for any problems you encounter in the upgrade before we publish the full stable release.

As we shared at React Conf, React 18 will include our new concurrent renderer and a gradual migration strategy for concurrent features. React 18 also includes a small number of breaking changes and features outside of concurrent rendering. In this post we will describe the changes to expect when upgrading to React 18.

Note for React Native users: React 18 will ship in React Native with the New React Native Architecture. For more information, see the React Conf keynote here.

Installing

To install the latest React 18 RC, use the @rc tag:

npm install react@rc react-dom@rc

Or if you’re using yarn:

yarn add react@rc react-dom@rc

New client root APIs

When you first install React 18, you will see a warning in the console:

Screenshot of React error with text

React 18 introduces a new root API which provides better ergonomics for managing roots. The new root API also enables the new concurrent renderer, which allows you to opt-into concurrent features.

To upgrade ReactDOM.render:

// Before
const container = document.getElementById('app');
ReactDOM.render(<App tab="home" />, container);

// After
const container = document.getElementById('app');
const root = ReactDOM.createRoot(container);
root.render(<App tab="home" />);

We’ve also changed unmountComponentAtNode to root.unmount:

// Before
unmountComponentAtNode(containter);

// After
root.unmount();

And removed the callback from render:

// Before
const container = document.getElementById('app');
ReactDOM.render(<App tab="home" />, container, () => {
  console.log('rendered');
});

// After
function AppWithCallbackAfterRender() {
  useEffect(() => {
    console.log('rendered');
  });

  return <App tab="home" />
}

const container = document.getElementById('app');
const root = ReactDOM.createRoot(container);
root.render(<AppWithCallbackAfterRender />);

Finally, to upgrade ReactDOM.hydrate:

// Before
const container = document.getElementById('app');
ReactDOM.hydrate(<App tab="home" />, container);

// After
const container = document.getElementById('app');
const root = ReactDOM.hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here.

For more information, see the working group discussion here.

Updates to server rendering APIs

In this release, we’re revamping our server rendering APIs to support Suspense on the server and Streaming SSR.

As part of these changes, the following APIs will continue working, but with limited support for Suspense. Instead of erroring, these APIs will server render the nearest fallback for any subtree that suspends, and retry rendering on the client:

  • renderToString
  • renderToStaticMarkup

For full support of Suspense on the server, React 18 introduces a new recommended API. This API will retry suspended Suspense boundaries on the server, streaming the server-rendered content to the client when it is available:

  • renderToPipeableStream

This new streaming API replaces the old streaming APIs, which are now deprecated and will warn:

  • renderToNodeStream
  • renderToStaticNodeStream

For more information on the changes to these APIs, see the working group post on Upgrading to React 18 on the server, a deep dive on the new Suspense SSR Architecture, and Shaundai Person’s talk on Streaming Server Rendering with Suspense at React Conf 2021.

Automatic batching

React 18 adds out-of-the-box performance improvements by doing more batching by default. Batching is when React groups multiple state updates into a single re-render for better performance. Before React 18, we only batched updates inside React event handlers. Updates inside of promises, setTimeout, native event handlers, or any other event were not batched in React by default:

// Before React 18 only React events were batched

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will render twice, once for each state update (no batching)
}, 1000);

Starting in React 18 with createRoot, all updates will be automatically batched, no matter where they originate from. This means that updates inside of timeouts, promises, native event handlers or any other event will batch the same way as updates inside of React events:

// After React 18 updates inside of timeouts, promises,
// native event handlers or any other event are batched.

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React will only re-render once at the end (that's batching!)
}, 1000);

This is a breaking change, but we expect this to result in less work rendering, and therefore better performance in your applications. To opt-out of automatic batching, you can use ReactDOM.flushSync:

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React has updated the DOM by now
  flushSync(() => {
    setFlag(f => !f);
  });
  // React has updated the DOM by now
}

For more information, see the Automatic batching deep dive.

New APIs for libraries

In the React 18 Working Group we worked with library maintainers to create new APIs needed to support concurrent rendering for use cases specific to their use case in areas like styles, external stores, and accessibility. To support React 18, some libraries may need to switch to one of the following APIs:

  • useID is a new hook for generating unique IDs on both the client and server, while avoiding hydration mismatches. This solves an issue that already exists in React 17 and below, but it’s even more important in React 18 because of how our streaming server renderer delivers HTML out-of-order. For more information see the useId post in the working group.
  • useSyncExternalStore is a new hook that allows external stores to support concurrent reads by forcing updates to the store to be synchronous. This new API is recommended for any library that integrates with state external to React. For more information, see the useSyncExternalStore overview post and useSyncExternalStore API details.
  • useInsertionEffect is a new hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. This hook will run after the DOM is mutated, but before layout effects read the new layout. This solves an issue that already exists in React 17 and below, but is even more important in React 18 because React yields to the browser during concurrent rendering, giving it a chance to recalculate layout. For more information, see the Library Upgrade Guide for <style>.

React 18 also introduces new APIs for concurrent rendering such as startTransition and useDeferredValue, which we will share more about in the upcoming stable release post.

Adding Strict Effects to Strict Mode

In the future, we’d like to add features to React which would allow a component to mount without immediately creating effects (such as pre-rendering), or to destroy effects in already-mounted components (such as when a component becomes no longer visible). These features will add better performance and resource management out-of-the-box to React, but require effects to be decoupled from the component lifecycle and resilient to being created and destroyed multiple times in a component.

To help surface these issues, React 18 introduced Strict Effects to Strict Mode. With Strict Effects, React will automatically destroy and re-create every effect in development, whenever a component mounts.

For more information, see the Working Group posts for Adding Strict Effects to Strict Mode and How to Support Strict Effects.

Testing

TODO:

Dropping support for Internet Explorer

In this release, React is dropping support for Internet Explorer, which is going out of support on June 15, 2022. We’re making this change now because new features introduced in React 18 are built using modern browser features such as microtasks which cannot be adequately polyfilled in IE.

If you need to support Internet Explorer we recommend you stay with React 17.

Other Changes

Changelog

TODO

Is this page useful?Edit this page