Skip to content

Latest commit

 

History

History
206 lines (134 loc) · 12.3 KB

0212-react-18.md

File metadata and controls

206 lines (134 loc) · 12.3 KB
  • Start Date: 2022-03-23
  • RFC PR: #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 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/client
    • createRoot
    • hydrateRoot
  • react-dom/server
    • renderToPipeableStream
    • renderToReadableStream

Additionally, we’ve added limited support for Suspense to:

  • renderToString
  • renderToStaticMarkup

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.

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.

useId

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.

Thanks @n8mellis for the original RFC for isomorphic IDs.

Drawbacks

Some of the links above include descriptions of drawbacks. Additionally, new React features that rely on concurrent rendering or streaming may impose additional limitations on the ecosystem libraries that want to be compatible with them. We’ve worked to address these concerns in the working group, but please let us know if you have feedback on a use case that isn’t covered.

Alternatives

Overall, the premise of these changes is that concurrent rendering unlocks powerful features that significantly improve the user experience, and yet feel natural to use to an application developer. You can watch the React 18 keynote to learn more about our vision.

An alternative would be to not pursue concurrent rendering as a unified strategy and instead try to solve each of the problems in isolation. So far, the programming model we’re pursuing has allowed us to make significant under-the-hood improvements (for example, streaming HTML and selective hydration) with barely any API changes, and then build more features (like transitions) on top. This makes us optimistic we’re on the right track.

Note: in some cases, based on feedback from the working group, entire APIs were redesigned. For example, we originally thought to solve the external store problem using the useMutableSource RFC, but re-designed it as useSyncExternalStore.

See the Working Group links in each section for the design decisions and alternatives that were considered per feature.

Adoption strategy

As shared in The Plan for React 18, based on feedback from the community, we’ve redesigned the upgrade strategy for concurrent features to allow for gradual adoption. That means, instead of an all-or-nothing “mode”, concurrent rendering will only be enabled for updates triggered by one of the new features. By default, React will continue to render the same as it did in React 17.

In practice, this means you will be able to adopt React 18 without rewrites and try the new features at your own pace.

How we teach this

We’re in the process of updating the docs to explain the new features and behavior. We’ve also worked with educators in the Working Group to understand how to teach these features as they were created, and speakers at React Conf 2021 on talks deep diving into the details.

Credits

Credit for these features are too many to count, but special thanks to the React 18 Working Group without whom this release would not be possible.