🔥

今話題となっているReact 19のSuspenseの変更内容を詳しく見てみました。

2024/06/14に公開

はじめに

以下のツイートが発端でした。

https://x.com/TkDodo/status/1800501040766144676

投稿者は、React Query のメインコントリビュータの tkdodo さんです。

React 19 に含まれる変更にある異変を気づきました。
変更は以下に該当します

react: Don’t prerender siblings of suspended component #26380

リリースノートに隅っこにありました。
https://react.dev/blog/2024/04/25/react-19-upgrade-guide#other-notable-changes

この変更は RFC なしで含まれました。該当 PR は以下
https://github.com/facebook/react/pull/26380

What

React では、 Suspense を使って、非同期処理を行うコンポーネントやReact.lazyによってコンポーネントの遅延ロード時に、読み込みを完了するまでフォールバックを表示させることができます。

以下のような実装があるとします。

App.tsx
import { lazy, Suspense, useState } from "react";

const AvatarComponent = lazy(() => import("./AvatarComponent"));
const InfoComponent = lazy(() => import("./InfoComponents.tsx"));
const MoreInfoComponent = lazy(() => import("./MoreInfoComponent.tsx"));

export default function App() {
  const [details, setDetails] = useState(false);
  return (
    <div className='App'>
      {!details && <button onClick={() => setDetails(true)}>CLICK ME</button>}
      {details && (
        <Suspense fallback={<div className='loader'></div>}>
          <AvatarComponent />
          <InfoComponent />
          <MoreInfoComponent />
        </Suspense>
      )}
    </div>
  );
}

コンポーネントたちを遅延ロードして、fetch されレンダリングされるまで、Suspense でロード中の状態を表示することをしています。

React 18 までは、2つのコンポーネントをまとめてひとつの Suspense に囲んでも、コンポーネントファイルを取得する fetch 処理は並行に行われました。
alt text

React 19 からはそうでなくなる予定です。
React 19 では、2つのコンポーネントをまとめてひとつの Suspense に囲むと、コンポーネントの fetch が並行に行われず、ウォーターフォール状態になります。
alt text

レンダリングが完了するまで以前より長かったこと体感できました。

一つの Suspense に囲んだコンポーネントの中で非同期処理を行うコンポーネントも影響しますね。
例は tkdodo さんが用意したものを参照します。

https://stackblitz.com/edit/tanstack-query-4j1mbu?file=src%2Findex.jsx

export default function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <React.Suspense fallback={<p>...</p>}>
        <RepoData name='tanstack/query' />
        <RepoData name='tanstack/table' />
      </React.Suspense>
    </QueryClientProvider>
  );
}

<RepoData/>というコンポーネントは、内側でuseSuspenseQueryを使って fetch を行なっています。

React 18 では並行に fetch を行なっています。
alt text

React 19 では、fetch が並行に行われず、ウォーターフォール状態になること確認できます。
alt text

これからどうするの

修正PR の記載の通り、fetch を hoist する、ようは Suspend をするコンポーネントのレンダリング処理以前に使うデータを取得しろのことですね。RSC 使える framework は、RSC で データを fetch。Client オンリーのアプリは、route loaders やハイレベルのコンポーネントで fetch を行うことを呼びかけています。
https://github.com/facebook/react/pull/26380

React チームがどう対応するかは、注目です 👀

GitHubで編集を提案

Discussion