React 18 brings several useful new features with no additional upgrade costs and is worth a closer look.

Here are a few key messages:

  • React 18 Working Group. Use the community to discuss the React 18 release pace and new features.
  • Release plan. It’s not officially released yet, but@alphaVersion is already available,Install the alpha.
  • React 18 Features. It’s not officially released yet, but the features are available first, and this week’s intensive reading will focus on the document.

Intensive reading

Overall, React 18 brings three new features:

  • Automatic batching.
  • Concurrent APIS.
  • SSR for Suspense.

A simple render function upgrade is also required to enable the new features.

Automatic batching

Batching means that React can combine multiple setState events in callback functions into a single render.

In other words, setState does not modify State in real time, but the combination of multiple setState calls triggers only one rendering, which can not only reduce the instability caused by the existence of intermediate values in the program data State, but also improve the rendering performance. This can be interpreted as the following code:

function handleClick() {
  setCount((c) = > c + 1);
  setFlag((f) = >! f);// Only trigger render once
}
Copy the code

However, before React 18, if the setState function was called asynchronously, the context was lost and the merge process could not be performed. Therefore, every setState call would immediately trigger a rerender:

function handleClick() {
  // React 18 earlier versions
  fetch(/ *... * /).then(() = > {
    setCount((c) = > c + 1); // Rerender immediately
    setFlag((f) = >! f);// Rerender immediately
  });
}
Copy the code

The optimization that React 18 brings is that you can merge renderings in any situation! Even if setState is called multiple times in a Promise, timeout, or event callback, it is merged into a single render:

function handleClick() {
  // React 18+
  fetch(/ *... * /).then(() = > {
    setCount((c) = > c + 1);
    setFlag((f) = >! f);// Only trigger render once
  });
}
Copy the code

If you want to rerender immediately after the setState call, just use the flushSync wrapper:

function handleClick() {
  // React 18+
  fetch(/ *... * /).then(() = > {
    ReactDOM.flushSync(() = > {
      setCount((c) = > c + 1); // Rerender immediately
      setFlag((f) = >! f);// Rerender immediately
    });
  });
}
Copy the code

To turn this feature on, replace reactdom. render with the reactdom. createRoot call.

New ReactDOM Render API

The upgrade is simple:

const container = document.getElementById("app");

/ / old render apis
ReactDOM.render(<App tab="home" />, container);

/ / new createRoot API
const root = ReactDOM.createRoot(container);
root.render(<App tab="home" />);
Copy the code

The main reason for the API change is again semantic, meaning that when we call Render multiple times, we no longer need to pass in the container argument repeatedly because container is already pre-bound to root in the new API.

Reactdom.hydrateroot is also replaced by reactdom.hydrateroot:

const root = ReactDOM.hydrateRoot(container, <App tab="home" />);
// Note that root.render() is not called
Copy the code

The advantage of this is that if root.render(
) is called later for re-rendering, we don’t care if the root is from createRoot or hydrateRoot, because the subsequent APIS behave the same, reducing the cost of understanding.

Concurrent APIS

First, understand what a Concurrent Mode is.

In simple terms, Concurrent Mode is an interruptible rendering design architecture. When do I interrupt rendering? When a higher-priority render arrives, perform the higher-priority render immediately by abandoning the current render in exchange for visually faster response times.

One might say, no, the CPU execution of the previous render is wasted when the render is interrupted, in other words, the overall execution is often increased. This is true, but the user’s perception of the timeliness of the page interaction is divided into two types: the first is the immediate input feedback, and the second is the side effects of the input feedback, such as updated lists. Even if the input feedback is satisfied first, even if the side effect feedback is slower, the experience will be better, not to mention that the side effect feedback is mostly nulled by the change in the input feedback.

React changes the render DOM tree mechanism to two bidirectional lists, and the render tree pointer has only one pointer to one of the lists. Therefore, the pointer pointer can be switched after the update is complete, and changes to the other tree can be abandoned at any time before the pointer is switched.

So that’s the background input. React 18 provides three new apis to support this mode:

  • StartTransition.
  • UseDeferredValue.
  • .

The last two documents are not available yet, so this article will only cover the first API: startTransition. First look at usage:

import { startTransition } from "react";

// Urgent update:
setInputValue(input);

// mark the update inside the callback as a non-urgent update:
startTransition(() = > {
  setSearchQuery(input);
});
Copy the code

In simple terms, renderings triggered by setState wrapped in the startTransition callback are marked as non-urgent renderings that may be preempted by other urgent renderings.

In this example, when setSearchQuery updates the list so much that it takes 100% of the CPU to render, the user makes another input that triggers the rendering caused by setInputValue. At this point, setSearchQuery rendering is stopped immediately in favor of setInputValue rendering, which allows the user’s input to be reflected quickly on the UI at the expense of slower search list responses. A broken transition can be accessed via isPending:

import { useTransition } from "react";
const [isPending, startTransition] = useTransition();
Copy the code

It more in line with the operating system design concept, we know that in the operating system is through the interrupt response of the underlying hardware events, interrupt is very urgent, because hardware can message queue storage is very limited, the operating system can’t response, even if the hardware input may be lost), thus to support preemptive kernel, And execute the interrupt as soon as it arrives (possibly moving less urgent operations to the bottom half).

For front-end interactions, “interrupts” from the user’s perspective typically come from keyboard or mouse actions, but unfortunately, front-end frameworks and even JS are too upper-level for them to automatically recognize:

  1. Which code is generated by an emergency interrupt. Such asonClickDoes it have to be a mouse click? Not necessarily, maybexxx.onClickActively triggered, not user-triggered.
  2. Does the user trigger an emergency interrupt? Not necessarily. For example, after typing,setInputValueIs urgent, while updating the query listsetSearchQueryIt’s not urgent.

We need to understand the limitations of the front-end scenario’s perception of user actions to understand why the urgency of updates must be manually specified, as opposed to an operating system where the upper level program does not need to sense the presence of interrupts.

SSR for Suspense

The full name is: Streaming SSR with Selective chocolate.

Build a continuous rendering pipeline from server to client like a stream, rather than a one-time rendering mechanism like renderToString. Selective hydration refers to the process of binding JS events to respond to user interactions or DOM updates after the backend content hits the front end. Before React 18, this operation had to be integrated and the hydration process may be slow. It can cause global lag, so selective hydration can be prioritized according to demand.

So this feature is actually for SSR, and the ability to enable vectors is Suspense (so don’t think of Suspense as just a loading function in the future). In Suspense, it was designed to solve server side rendering problems, but at first it only implemented the client test on demand functionality. In Suspense, you will find that the React team gradually adds more power to Suspense.

Suspense SSR for resolving three main problems:

  • In SSR mode, if the efficiency of different modules is different, the whole HTML throughput time will be slowed down by the slowest module, which may lead to a better experience than non-SSR. To take an extreme case, if a component in the report relies on slow queries and takes five minutes for data to come out, SSR results in a white screen of five minutes.
  • Even though THE SSR content is posted on the page, JS is not loaded, so there is no interaction in the whole page.
  • Even after JS is loaded, due to the fact that only the whole encounter can be carried out before React 18, the first interaction response may be delayed.

In Server Render of React 18, you can solve the above three problems by using pipeToNodeWritable instead of renderToString and working with Suspense.

Use pipeToNodeWriteable to see this example.

The biggest difference is that server-side rendering is changed from simple res.send to res.socket, which changes rendering from a single action to a persistent action.

So how does React 18 SSR work? It is recommended to have a look at the diagram of this introduction document. It is very intuitive. Here I will briefly describe it:

  1. be<Suspense>Wrapped blocks that do not block the first swallow when rendered on the server and are hit in real time after the block is ready (including asynchronous fetch) to the page (in HTML mode, at this point without hydration)fallbackThe content of the.
  2. Hydration is also gradual, so that the page doesn’t get stuck implementing all the full jS at once (after all, it’s just a few callback registrations in React, Hooks of sorts, a huge amount of apps).
  3. After being split into several variations, React also monitors mouse clicks in advance, prioritization of click regions, and even preempts ongoing variations of Chocolate in other regions.

So to sum up, the new SSR performance improve the secret lies in two words: on demand.

Before React 18, the process from back end to front end was not optimized at all. Now, THE THROUGHPUT of SSR HTML is changed to multiple, on-demand, and preemption is supported during hydration, which further improves performance.

conclusion

Taken together, React 18 focuses on faster performance and more responsive user interactions, with interrupt and preemption concepts all over the place.

In the future, when it comes to front-end performance optimization, we will have more perspectives from the application side (not just from the engineering perspective), and effectively improve the interactive feedback speed from the following two perspectives:

  1. The framework design is interrupted at any time, and the first priority is rendering the UI interaction modules that the user cares most about.
  2. A “smooth” pipeline SSR from back to front, and changes the hydration process on demand, with support punctuated by higher-priority user interactions, the portion of the first-priority hydration user is interacting with.

React 18 · Issue #336 · dt-fe/weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.

Pay attention to the front end of intensive reading wechat public account

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)