What s new in React 18

Mondo Technology Updated on 2024-02-12

New feature: Automatic batchingBatching is when React groups multiple state updates into a single re-render for better performance. If there is no automatic batching, we only batch the updates in the react event handler. By default, React doesn't get right.

Promises, settimeouts, native event handlers, or any other event. With automated batch processing, these updates will be processed automatically in batches.

Before: Batching is only performed on react events settimeout(()=>, 1000); After: Updates within timeouts, promises, native event handlers, or any other event are batched. settimeout(()=> ,1000);
What's new: Transitions are a new concept in React that distinguishes between urgent and non-urgent updates.

Emergency updates reflect direct interactions like typing, tapping, pressing, and so on. Transition updates transition the UI from one view to another. Urgent updates like typing, tapping, or pressing need to be responded to immediately to match our intuition about how physical objects behave. Otherwise, they will feel "something is wrong". However, the transition is different because the user doesn't expect to see every intermediate value on the screen.

For example, when you select a filter in a drop-down menu, you want the filter button itself to react immediately when you click on it. However, actual results may be transitioned individually. A small delay will be imperceptible and often expected. And, if you change the filter again before the result is rendered, you only care about seeing the latest results.

In general, for the best user experience, a user input should result in both an emergency update and a non-emergency update. You can use the StartTransition API in an input event to tell React which are emergency updates and which are "transitions":

import from 'react';Urgent: Displays the input content setinputvalue(input); Mark any state updates inside as transitiontransition(()=>).
Updates wrapped in StartTransition are treated as non-urgent and will be interrupted if a more urgent update, such as a click or keystroke. If a transition is interrupted by the user (e.g., entering multiple characters in a row), React discards the unfinished invalid render and renders only the latest update.

UseTransition: A hook that starts the transition, including a value to track pending state. starttransition: A method to start a transition when the hook is unusable. The transition will choose concurrent rendering, which allows the update to be interrupted. If the content is suspended again, the transition also tells React to continue displaying the current content while rendering the transition content in the background.

The new Suspense feature allows you to declaratively specify the loading state if a part of the component tree isn't ready to be displayed

Suspense makes "UI loading state" the first type of declarative concept in React's programming model. This allows us to build a higher level of functionality on top of it. 

A few years ago, we launched a limited version of Suspense. However, the only supported use case is with reactlazy split** and is not supported at all when rendering on the server side.

In React 18, we've added server-side support for Suspense and extended its functionality with concurrent rendering.

Suspense in React 18 works best when combined with a staging API. If you hang during a transition, React will prevent content that is already visible from being replaced by a fallback. Instead, React delays rendering until there is enough data to load to prevent a bad loading state.

New client-side and server-side rendering APIsIn this release, we've taken the opportunity to redesign the APIs we've exposed for client-side and server-side rendering. These changes allow users to continue using the old APIs in React 17 mode when upgrading to the new APIs in React 18.

react dom client

These new APIs are now exported from the react-dom client:

createroot: A new way to create a root to render or unmount. Use it as an alternative to reactdomrender。Without it, the new features of React 18 wouldn't work. hydrateroot: A new method for creating server-side rendering applications. Use it as an alternative to reactdomHydrate works with the new React DOM server-side API. Without it, the new features of React 18 wouldn't work. Both CreateRoot and HydrateRoot accept a new option called OnRecoverableError, in case you want to be notified when React Render or Hydrate recovers from an error for documentation. By default, React uses.

reporterror, or use the console in older browserserror。

react dom server

These new APIs are now exported from react-dom server and have full support for streaming Suspense::

rendertopipeablestream: used in the Node environment. RenderToReadableStream: Used in modern edge runtime environments such as Deno and Cloudflare Workers. Existing rendertostring methods are still available, but discouraged.

New strict mode behavior In the future, we want to add a feature that allows React to add and remove parts of the UI while preserving state. For example, when a user no-clips from one screen and switches back, React should be able to immediately display the previous screen. To do this, React will unload and reload the tree using the same component state as before.

This feature will give React apps better out-of-the-box performance, but requires the component to be resilient to effects being loaded and destroyed multiple times. Most effects will work without any changes, but some assume they are only loaded or destroyed once.

To help surface these issues, React 18 introduces a new development-only check for strict mode. Whenever a component is mounted for the first time, this check will automatically unload and reload each component, and return to its previous state on the second mount.

Prior to this change, React would load the component and create an effect:

React Mounts Component Layout Effect Creation Effect Creation
In React 18's strict mode, React simulates unloading and reloading components in development mode:

* react mount component * layout effect creation * effect creation * react mock unloading component * layout effect destruction * effect destruction * react simulate loading component (using the previous state) * layout effect creation * effect creation
New hooksuseid

UseId is a new hook that is used to generate a unique ID on the client and server to avoid hydrate mismatches. It is primarily used for component libraries that integrate accessibility APIs that require a unique ID. This solves an issue that already exists in React 17 and below, but in React

18 is even more important because of the new streaming server-side renderer's unordered delivery of HTML.

usetransition

UseTransition and StartTransition let you mark some status updates as not urgent. Other status updates are considered urgent by default. react

Urgent status updates (for example, updating a text input) will be allowed to interrupt non-urgent status updates (for example, rendering a list of search results).

usedeferredvalue

useDeferredValue lets you postpone re-rendering the non-urgent part of the tree. It is similar to Debounce but has some advantages over it. It doesn't have a fixed time delay, and React will attempt to delay the rendering as soon as the first render is reflected on the screen. Deferred rendering is interruptible, and it doesn't block user input.

usesyncexternalstore

UseSyncExternalStore is a new hook that allows external storage to support concurrent reads by forcing updates to Store for synchronization. It eliminates the need for a useeffect when implementing a subscription to an external data source and is recommended for anything related to React

Libraries for external state integration.

useinsertioneffect

UseInsertionEffect is a new hook that allows the css-in-js library to solve performance issues with injecting styles in rendering. Unless you've already built a css-in-js library, we don't want you to use it. This hook will run after the dom is changed, but in the .

layout effect before reading the new layout. This solves an issue that was already in React 17 and below, but is even more important in React 18 because React concedes to the browser when rendering concurrently, giving it a chance to recalculate the layout.

Concurrent Mode (CM) is a concept that we've probably heard many times, but in fact the concept has matured in the last year, and in React 17 it was possible to turn on CM through some experimental APIs.

Concurrent mode helps your app stay responsive and adjust appropriately based on the user's device performance and internet speed, and it fixes blocking rendering limitations by making rendering interruptible. In the Concurrent pattern, React can update multiple states at the same time.

It may be a bit awkward to say it too complicated, but it can be summed up in one sentence:The difference between React 17 and React 18 is that it goes from synchronous non-interruptible updates to asynchronous interruptible updates.

To better manage root nodes, React 18 introduces a new root API that also supports the New Concurrent Renderer, which allows you to enter Concurrent Mode.

// react 17import react from 'react';import reactdom from 'react-dom';import app from './app';const root = document.getelementbyid('root')!;reactdom.render(, root);// react 18import react from 'react';import reactdom from 'react-dom/client';import app from './app';const root = document.getelementbyid('root')!;reactdom.createroot(root).render();
In React 18, the new root API is provided, and we just need to upgrade render to createroot(root).render() to enable concurrency mode.

At this time, some students may ask: Does turning on concurrency mode mean that concurrent updates are enabled?

no!In some experimental features in React 17, enabling concurrent mode means enabling concurrent updates, but after the official release of React 18, due to official policy adjustments, React no longer relies on concurrent mode to enable concurrent updates.

In other words:Concurrent mode is enabled, but concurrent updates are not necessarily enabled!

In a word:In 18, there are no longer multiple modes, but rather whether or not to enable concurrent updates is based on whether or not to use concurrency features.

From an architectural perspective, there are currently two architectures:

Updates are made in a non-interruptible recursive mannerstack reconciler(The old architecture) is updated using interruptible traversalfiber reconcilerThe new architecture can choose whether to enable concurrent updates or not, so there are four situations for all React versions on the market today:

The old architecture (v15 and earlier versions) new architecture, no concurrent updates are enabled, the behavior is the same as in case 1 (this is the case in v16 and v17 by default) the new architecture, concurrent updates are not enabled, but concurrent mode and some new features are enabled (such as automatic batching, which is the case in v18 by default) The new architecture, concurrent mode is enabled, and concurrent updates are enabledConcurrency features are features that can be used only after concurrency mode is enabled, such as:

UseDeFerRedValueUseTransitionStartTransition concurrency is an example of a new API that can dramatically improve user interaction by marking a specific update as a "transition", simply because renders triggered by a setstate wrapped in StartTransition are marked as non-urgent renders that may be preempted by other emergency renders.

import react, from 'react';const app: react.fc = ()=> )return ( 

}export default app;

UseDeferRedValue Concurrency Feature: Example: From the introduction, does useDeferredValue and UseTransition feel similar?

Same: UseDeferredValue is essentially the same as the internal implementation of UseTransition, which is tagged as a deferred update task. Different: UseTransition turns the update task into a deferred update task, and useDeferredValue

is to generate a new value, which acts as a delay state. (One is used to wrap the method, and the other is used to wrap the value).

So, in the above starttransition example, we can also use useDeferredValue:

import react, from 'react';const app: react.fc = ()=> , using the concurrency feature, enabling concurrent updates const deferredlist = usedeferredvalue(list);  return ( 

}export default app;

At this point, our task is split into different tasks for each frame, and the JS script execution time is about 5ms, so that the browser has time left to perform style layout and style drawing, reducing the possibility of frame drops.

React 18 achieves out-of-the-box performance improvements by executing batch processing by default.

Batch processing refers to batching multiple state updates into a single update at the data layer for better performance (at the view layer, merging multiple renders into a single render).

Before React 18: There were some cases where updates weren't mergedPrior to React 18, we only did batch updates in React event handlers. By default, inpromises, settimeouts, and native event handlers, or anyWhat other eventsNone of the updates will be batched:

Case 1: React event handler

The following ** will be batched and will only render the page once.

import react, from 'react';Prior to react 18, const app: reactfc = ()=> }count2 is $`export default app;
Scenario 2: settimeout

If we put the update of the state inpromiseorsettimeoutInside, the components are rendered twice and do not be updated in batches.

import react, from 'react';Prior to react 18, const app: reactfc = ()=> ) will not be batched in settimeout }}count1:

count2:

}export default app;

Scenario 3: Native JS events

In a native JS event, the result is the same as case two, and each time you click update two states, the component renders twice and does not do a batch update.

import react, from 'react';Prior to react 18, const app: reactfc = ()=> ) will not be batched in native js events }, return (count1:

count2:

}export default app;

In React 18: Merge Updates In React 18, the three examples above will only have one render, as all updates will be batched automatically. This is undoubtedly a good improvement in the overall performance of the application.

However, the following example will execute render twice in React 18

import react, from 'react';// react 18const app: react.fc = ()=> }count1: 

count2:

}export default app;

Summary:

Before 18, batching was only done automatically in react event handlers, otherwise it would be updated multiple times after 18, and in any case it would be automatically batched, merging multiple updates into one batch was a breaking change, if you want to opt out of bulk updates, you can use flushsync:

import react, from 'react';import from 'react-dom';const app: react.fc = ()=> ) 1st update flushsync(()=> ) 2nd update }}count1:

count2:

}export default app;

Suspense no longer needs a fallback to capture empty fallback properties, and the handling of the property has changed: the suspense boundary for missing or null fallbacks is no longer skipped.

Before updating

Previously, if your Suspense component didn't provide a fallback attribute, React would quietly skip it and continue searching up for the next boundary:

react 17const app = ()=> > < - this border is used, showing loading component < - this border is skipped, no fallback attribute ); export default app;
After updating

React will now use the current component's suspense as the boundary, even if the current component's suspense has a value of null or undefined:

react 18const app = ()=> > < - does not use < - this border is used, rendering fallback as null ); export default app;
The return value of a React component is in React 17, and if you need to return an empty component, React only allows null. If you explicitly return undefined, the console will throw an error at runtime. In react 18, it is no longer checked for crashes due to returning undefined. It can return both null and undefined (but the DTS file in React 18 will still check and only allow null to be returned, so you can ignore this type error). The meaning of concurrent updates is to alternate different tasks, when the reserved time is not enough, React returns control of the thread to the browser, waits for the next frame time to arrive, and then continues the interrupted workConcurrent mode is the basic premise of implementing concurrent updatesTime slicing is the specific means to achieve concurrent updates.

Related Pages