- Start Date: 2022-03-23
- RFC PR: https://github.com/reactjs/rfcs/pull/212
- React Issue: N/A
Note: This RFC is closer to an "intent to ship" and is different than the process we typically do because it is the result of years of research into concurrency, Suspense, and server rendering. All of what is posted here was designed and discussed over the last year in the React 18 Working Group (available here) and iterated on in public experimental releases since 2018. We'd like to get one final round of broad public feedback from the community before shipping in case there are new concerns that have not been discussed before. You can consider the Working Group to be a part of the RFC, so please feel free to quote and discuss any content from it when commenting on the RFC here.
RFC: Intent to ship React 18
Summary
This RFC describes the changes we intend to ship in React 18 including:
- New feature: automatic batching
- New feature: transitions
- New Suspense features
- New client and server rendering APIs
- New Strict Mode behaviors
- New hooks
Motivation
The goal of React 18 is to release the new concurrent rendering APIs that we are building all future React features on top of including Suspense, transitions, streaming server rendering, server components, and more. This release does not include all of the features on day one, but by upgrading your apps fully to React 18 using the new client and server rendering APIs, you will be ready to gradually adopt new future features as they become available.
While the focus of this release is on upgrading to the new rendering engine, we’re also making a few long-needed improvements (such as automatic batching, Suspense on the server, and useId), adding new hooks for libraries to support concurrent rendering (such as useSyncExternalStore and useInsertionEffect), and a few new concurrent features (such as useTransition and useDeferredValue).
Detailed Design
New feature: Automatic Batching
Batching is when React groups multiple state updates into a single re-render for better performance. Without automatic batching, 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. With automatic batching, these updates will be batched automatically:
// Before: only React events were batched.
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will render twice, once for each state update (no batching)
}, 1000);
// After: updates inside of timeouts, promises,
// native event handlers or any other event are batched.`
setTimeout(() => {
setCount(c => c + 1);
setFlag(f => !f);
// React will only re-render once at the end (that's batching!)
}, 1000);
New feature: transitions
Transitions allow users to tell React that the contained updates are less urgent than other work. For example, handling user text input is more urgent than UI transitions. To support these feature, we’re adding two new APIs:
useTransition: a hook to start transitions, including a value to track the pending state.startTransition: a method to start transitions when the hook cannot be used.
Transitions will opt in to concurrent rendering, which allows the update to be interrupted. If the content re-suspends, transitions also tell React to continue showing the current content while rendering the transition content in the background (see the Suspense RFC for more info).
- New feature: startTransition
- Real world example: adding startTransition for slow renders
- Patterns for startTransition
New Suspense features
These features are outlined in a separate RFC.
New client and server rendering APIs
In this release we took the opportunity to redesign the APIs we expose for rendering on the client and server. These changes allow users to continue using the old APIs in React 17 mode while they upgrade to the new APIs in React 18.
On the client, these APIs are:
react-dom/clientcreateRoothydrateRoot
react-dom/serverrenderToPipeableStreamrenderToReadableStream
Additionally, we’ve added limited support for Suspense to:
renderToStringrenderToStaticMarkup
And have deprecated:
renderToNodeStream
Note: these APIs are designed to be upgraded at the same time. For example, upgrading to createRoot on the client, but keeping renderToString on the server is unsupported.
- Replacing render with createRoot
- Moving create/hydrateRoot to react-dom/client
- Upgrading to React 18 on the client
- Upgrading to React 18 on the server
New Strict Mode behaviors
In the future, we’d like to add a feature that allows React to add and remove sections of the UI while preserving state. For example, when a user tabs away from a screen and back, React should be able to immediately show the previous screen. To do this, React would unmount and remount trees using the same component state as before.
This feature will give React apps better performance out-of-the-box, but requires components to be resilient to effects being mounted and destroyed multiple times. Most effects will work without any changes, but some effects assume they are only mounted or destroyed once.
To help surface these issues, React 18 introduces a new development-only check to Strict Mode. This new check will automatically unmount and remount every component, whenever a component mounts for the first time, restoring the previous state on the second mount.
Before this change, React would mount the component and create the effects:
* React mounts the component.
* Layout effects are created.
* Effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
* React mounts the component.
* Layout effects are created.
* Effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effects are created.
* Effects are created.
New hooks
useDeferredValue
useDeferredValue is a new hook that accepts a value and returns a new copy of the value that will defer to more urgent updates. If the current render is the result of an urgent update, like user input, React will return the previous value and then render the new value after the urgent render has completed.
This hook is similar to user-space hooks which use debouncing or throttling to defer updates. The benefits to using useDeferredValue is that React will work on the update as soon as other work finishes (instead of waiting for an arbitrary amount of time), and like startTransition, deferred values can suspend without triggering an unexpected fallback for existing content.
useSyncExternalStore
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.
More details on this API are available in a separate RFC.
useInsertionEffect
useInsertionEffect is a new hook that allows CSS-in-JS libraries to address performance issues of injecting styles in render. Unless you’ve already built a CSS-in-JS library we don’t expect you to ever use this. 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.