11.1 Suspense

Suspenseは、子ツリーの準備が整うまでフォールバックを表示する境界である。

<Suspense fallback={<Spinner />}>
  <Comments />
</Suspense>

Suspense対応対象:

  • lazyで読み込むコード
  • Suspense対応フレームワークのデータ
  • useで読み取るPromise
  • Server Componentsストリーム

任意のfetchuseEffectで呼ぶだけではSuspense対応にならない。

11.2 lazy

const SettingsPage = lazy(() => import("./SettingsPage"));

function App() {
  return (
    <Suspense fallback={<p>読み込み中...</p>}>
      <SettingsPage />
    </Suspense>
  );
}

11.3 use

function Message({ messagePromise }: {
  messagePromise: Promise<string>;
}) {
  const message = use(messagePromise);
  return <p>{message}</p>;
}

Promiseが未解決なら、最寄りのSuspense境界へ処理を移す。

レンダリング中に毎回新しいPromiseを作ると安定しない。Server Component、フレームワークキャッシュ、親から渡されたPromiseなどを使う。

11.4 useTransition

緊急性の低い更新をTransitionとして扱う。

const [isPending, startTransition] = useTransition();

function selectTab(nextTab: string) {
  startTransition(() => {
    setTab(nextTab);
  });
}

入力値そのものの更新など即時反映が必要な処理をTransitionにしない。

11.5 useDeferredValue

const deferredQuery = useDeferredValue(query);

const results = useMemo(
  () => searchLargeList(deferredQuery),
  [deferredQuery]
);

入力は即時更新し、重い結果表示は遅延させる。

11.6 Suspense境界設計

  • ローディング体験のまとまりで配置する
  • 小さく分けすぎてちらつかせない
  • 画面全体を単一境界に閉じ込めない
  • Error Boundaryと組み合わせる
  • SSRストリーミングの単位として考える