Skip to content

Commit

Permalink
RFC: Intent to ship React 18
Browse files Browse the repository at this point in the history
This RFC describes the changes we intend to ship in React 18.
  • Loading branch information
rickhanlonii authored Mar 23, 2022
1 parent f744f67 commit 0f0ba48
Showing 1 changed file with 189 additions and 0 deletions.
189 changes: 189 additions & 0 deletions text/0000-react-18.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
- Start Date: 2022-03-23
- RFC PR: (leave this empty)
- React Issue: (leave this empty)

# 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

*This RFC is different than the process we typically do because it is the result of years of research into concurrency, Suspense, and server rendering. **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.** All of what is posted here was designed and discussed over the last year in the React 18 Working Group ([available here](https://github.com/reactwg/react-18/)). 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.*

## 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:

```js
// 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);
```

* [Automatic batching for fewer renders in React 18](https://github.com/reactwg/react-18/discussions/21)

### 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](https://github.com/reactwg/react-18/discussions/41)
* [Real world example: adding startTransition for slow renders](https://github.com/reactwg/react-18/discussions/65)
* [Patterns for startTransition](https://github.com/reactwg/react-18/discussions/100)

### 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.

* [Replacing render with createRoot](https://github.com/reactwg/react-18/discussions/5)
* [Moving create/hydrateRoot to react-dom/client](https://github.com/reactwg/react-18/discussions/125)
* [Upgrading to React 18 on the client](https://github.com/reactwg/react-18/discussions/6)
* [Upgrading to React 18 on the server](https://github.com/reactwg/react-18/discussions/22)

### 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.
```

* [Adding Strict Effects to Strict Mode](https://github.com/reactwg/react-18/discussions/19)
* [How to support strict effects](https://github.com/reactwg/react-18/discussions/18)
* [Testing with strict effects](https://github.com/reactwg/react-18/discussions/17)

### New hooks

#### 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.

* [Concurrent React for Library Maintainers](https://github.com/reactwg/react-18/discussions/70)
* [useMutableSource → useSyncExternalStore](https://github.com/reactwg/react-18/discussions/86)

#### 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.

* [Library Upgrade Guide: <style> (most CSS-in-JS libs)](https://github.com/reactwg/react-18/discussions/110)
* [Add useInsertionEffect](https://github.com/facebook/react/pull/21913)

#### 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.

* [Intent to Ship: useId](https://github.com/reactwg/react-18/discussions/111)

Thanks @n8mellis for the original [RFC for isomorphic IDs.](https://github.com/reactjs/rfcs/pull/32)

## 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](https://www.youtube.com/watch?v=FZ0cG47msEk) 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](https://github.com/reactjs/rfcs/pull/147), 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](https://reactjs.org/blog/2021/06/08/the-plan-for-react-18.html), 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.

* [What happened to concurrent “mode”](https://github.com/reactwg/react-18/discussions/64)

## 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.

* [React Working Group Q&A](https://github.com/reactwg/react-18/discussions/categories/q-a)
* [Glossary + Explain Like I’m Five](https://github.com/reactwg/react-18/discussions/46)
* [React Conf 2021](https://conf.reactjs.org/stage)

## 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.

0 comments on commit 0f0ba48

Please sign in to comment.