React18にアップグレードする方法

これは、 Reactの公式2022.03.08によって公開された記事「React18リリース候補にアップグレードする方法」の翻訳です。この記事を通じて、React18の新機能を包括的に理解することができます。

次に、React 18をより良い姿勢で使用するために、他のいくつかのより重要なReact 18の記事を翻訳し、迷子にならないように注意します。

本日、React18RCリリースをリリースしました。React Confで共有したように、React 18は並行モードに基づいており、インクリメンタルアップグレードの方法を提供しながら、より多くの機能を提供します。この記事では、React18にアップグレードするためのステップバイステップを紹介します。

インストール

@rcタグを使用して、Reactの最新バージョンをインストールします

## npm
$ npm install react@rc react-dom@rc

## yarn
$ yarn add react@rc react-dom@rc
复制代码

クライアント側のレンダリングAPIの更新

React 18を最初にインストールすると、次の警告が表示されます

ReactDOM.renderはReact18ではサポートされなくなりました。代わりにcreateRootを使用してください。新しいAPIに切り替えるまで、アプリはReact 17を実行しているかのように動作します。詳細:reactjs.org/link/switch…

React 18は、より合理的な初期化APIを提供します。このAPIを使用すると、並行モードが自動的に有効になります。

// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render(<App tab="home" />, container);

// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = createRoot(container);
root.render(<App tab="home" />);
复制代码

同時に、アンインストール方法unmountComponentAtNodeをからroot.unmount:に変更しました。

// Before
unmountComponentAtNode(container);

// After
root.unmount();
复制代码

Susponseを使用するときに問題があったためReactDOM.render関数ました。callback

// Before
const container = document.getElementById('app');
ReactDOM.render(<App tab="home" />, container, () => {
  console.log('rendered');
});

// After
function AppWithCallbackAfterRender() {
  useEffect(() => {
    console.log('rendered');
  });

return <App tab="home" />
}

const container = document.getElementById('app');
const root = ReactDOM.createRoot(container);
root.render(<AppWithCallbackAfterRender />);
复制代码

最后,如果你使用 hydration 来实现了 SSR,需要将 hydrate 替换为 hydrateRoot

// Before
import { hydrate } from 'react-dom';
const container = document.getElementById('app');
hydrate(<App tab="home" />, container);

// After
import { hydrateRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here.
复制代码

更多信息可见 Replacing render with createRoot

SSR API 更新

在 React 18 中,为了支持服务端的 Suspense 和流式 SSR,优化了 react-dom/server 的 API。

使用以下 API,将会抛出警告:

  • renderToNodeStream:废弃 ⛔️️

相反,对于 Node 环境中的流式传输,请使用:

  • renderToPipeableStream:新增

我们还引入了一个新的 API,以在现代边缘运行时环境支持流式 SSR 和 Suspense,例如 Deno 和 Cloudflare workers:

  • renderToReadableStream:新增

下面的两个 API 可以继续使用,但是不支持 Suspense:

  • renderToString:限制 ⚠️
  • renderToStaticMarkup:限制 ⚠️

下面的 API 没有变化:

  • renderToStaticNodeStream

更多信息可见Upgrading to React 18 on the serverNew Suspense SSR Architecture in React 18

自动批处理 Automatic Batching

批处理是指:React 将多个状态更新,聚合到一次 render 中执行,以提升性能。

在 React 18 之前,只能在 React 自己的事件机制中使用批处理,而在 Promise、setTimeout、原生事件等场景下,是不能使用批处理的。

React 18 支持了更多场景下的批处理,以提供更好的性能。

// 在 React 18 之前,只有 React 事件,才会使用批处理

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 只会 re-render 一次,这就是批处理
}

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 会 render 两次,每次 state 变化更新一次
}, 1000);
复制代码

使用 createRoot初始化 React 18 之后,所有的状态更新,会自动使用批处理,不关心应用场景。

// React 18 之后,Promise、setTimeout、原生事件中,都会自动批处理

function handleClick() {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 只会 re-render 一次,这就是批处理
}

setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 只会 re-render 一次,这就是批处理
}, 1000);
复制代码

这是一个 break change,但是我们希望这能提升你的产品性能。当然,你仍然可以使用 flushSync 来手动取消批处理,强制同步执行:

import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCounter(c => c + 1);
  });
  // React 更新一次 DOM
  flushSync(() => {
    setFlag(f => !f);
  });
  // React 更新一次 DOM
}
复制代码

更多信息可见 Automatic batching for fewer renders in React 18

三方库 API

在 React 18 中,我们和三方库作者合作,定义了一些新的 API,以满足三方库在 concurrent 模式下特定场景的诉求。比如 styles 管理、外部状态管理、可访问性(accessibility)等场景。

为了支持 React 18,一些三方库可能需要用到下面的 API:

  • useId 是一个新的 Hook,支持在客户端和服务端生成唯一的 ID,同时避免 hydration 的不兼容。它可以解决在 React 17 及更低版本一直存在的问题。在 React 18 中,这个问题尤为重要,因为流式 SSR 返回的 HTML 片段是无序的。更多信息可见 Intent to Ship: useId

  • useSyncExternalStore是一个新的 Hook,允许外部状态管理器,强制立即同步更新,以支持并发读取。这个新的 API 推荐用于所有 React 外部状态管理库。详情见 useSyncExternalStore overview postuseSyncExternalStore API details

  • useInsertionEffect是一个新的 Hook,它可以解决 CSS-in-JS 库在渲染中动态注入样式的性能问题。除非你已经构建了一个 CSS-in-JS 库,否则我们不希望你使用它。这个 Hook 执行时机在 DOM 生成之后,Layout Effect 执行之前。更多信息可见 Library Upgrade Guide for style

React 18还为 concurrent 渲染引入了新的 API,例如 startTransitionuseDeferredValue,在即将发布的稳定版本中会分享更多相关内容。

严格模式 Strict Mode

未来,我们希望添加一个功能,允许 React 保存组件的状态,但移除 UI 部分。比如在返回旧的页面时,React 立即恢复之前的内容。为此,React 将使用之前保留的状态重新加载组件。

这个功能会给 React 项目带来非常好的体验,但要求组件支持 state 不变的情况下,组件多次卸载和重载。

为了检查出不合适的组件写法,React 18 在开发模式渲染组件时,会自动执行一次卸载,再重新加载的行为,以便检查组件是否支持 state 不变,组件卸载重载的场景。

在以前,React 加载组件的逻辑为:

* React mounts the component.
    * Layout effects are created.
    * Effect effects are created.
复制代码

在 React 18 严格模式的开发环境,React 会模拟卸载并重载组件:

* React mounts the component.
    * Layout effects are created.
    * Effect 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 effect setup code runs
    * Effect setup code runs
复制代码

更多信息可见: Adding Strict Effects to Strict ModeHow to Support Strict Effects

配置测试环境

当你第一次在测试用例中使用 createRoot时候,你会看到以下警告:

The current testing environment is not configured to support act(…)

为了修复这个问题,你需要在执行用例之前设置 globalThis.IS_REACT_ACT_ENVIRONMENTtrue

// In your test setup file
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
复制代码

このタグは、単体テストのような環境で実行されていることをReactに通知します。Actの使用を忘れた場合、Reactはいくつかの役立つ警告を出力します。フラグをfalseに設定して、行為が不要であることをReactに通知することもできます。これは、ブラウザ環境をシミュレートするエンドツーエンドのテストに役立ちます。もちろん、テストライブラリがこの構成を自動的に追加することを願っています。たとえば、React Testing Libraryの次のバージョンには、追加の構成なしでReact18のサポートが組み込まれています。

詳細については、こちらをご覧ください:行為テストAPIと関連する変更の背景

IEのサポートを削除

このリリースでは、ReactはInternetExplorerのサポートを終了します。React 18で導入された新機能は最新のブラウザー開発に基づいており、マイクロタスクなどの一部の機能はIEでサポートされていないため、この変更を行いました。

Internet Explorerをサポートする必要がある場合は、引き続きReact17を使用することをお勧めします。

その他の変更

おすすめ

転載: juejin.im/post/7078601734716653604