React Suspense

読み込み待ちの UI を境界ごとに扱う Suspense の役割と使い方を学ぶ。

Suspense は、配下の UI がまだ表示できない間に fallback を表示する境界です。読み込み中の画面をどこで切り替えるかを、コンポーネントの構造に沿って表現できます。

lazy と組み合わせる

コード分割したコンポーネントを lazy で読み込むとき、Suspense が読み込み中の表示を受け持ちます。

import { lazy, Suspense } from 'react';

const Chart = lazy(() => import('./Chart'));

function Dashboard() {
  return (
    <Suspense fallback={<p>グラフを読み込み中です…</p>}>
      <Chart />
    </Suspense>
  );
}

Chart の読み込みが終わるまで fallback が表示され、準備できると Chart に切り替わります。

Suspense がすること・しないこと

Suspense は、それ自体でデータ取得を始める仕組みではありません。また、useEffect の中で始めたデータ取得の待機を自動的に検出するものでもありません。

Suspense 対応のデータソースや lazy と組み合わせて、待機中の UI を表現します。読み込み境界を小さくしすぎると画面が細かく切り替わり、大きすぎると必要な部分まで待たせます。利用者にとって自然な単位で境界を置くことが大切です。

fallback を設計する

fallback は単なるスピナーではなく、読み込み中でも画面の構造を伝える UI です。カードや表なら、完成形に近い骨組みを見せるスケルトンも選択肢になります。待機中に何が起きているかをわかりやすく示しましょう。

境界はユーザーの体験単位で置く

ページ全体を一つの Suspense で囲むと、少し遅い部品のために画面全体が fallback へ切り替わることがあります。一方で境界を細かくしすぎると、あちこちで別々のローディング表示が動き、画面のまとまりが失われます。

function ProfilePage() {
  return (
    <>
      <ProfileHeader />
      <Suspense fallback={<ActivitySkeleton />}>
        <ActivityFeed />
      </Suspense>
    </>
  );
}

この例では、すぐ表示できるプロフィールは残し、準備できていないアクティビティだけを待機表示にします。デザイナーやプロダクトの要求と合わせて、「どこまでが一つの完成した体験か」を基準に境界を決めます。

Error Boundary とは役割が異なる

Suspense が扱うのは、準備できるまでの待機状態です。読み込みの失敗やレンダー中の例外を表示するためには Error Boundary が必要です。待機と失敗を同じ fallback に押し込めず、それぞれの状態に合った UI を設計します。

読み込み中: Skeleton や「読み込み中」
読み込み失敗: 再試行ボタンと説明
空の結果: 次にできる操作の案内

state が保たれるとは限らない

初めて表示しようとした子コンポーネントが suspend した場合、そのレンダーで作られた state は保存されず、再試行時に初期状態からやり直されることがあります。読み込み中にも維持したい入力や選択があるなら、境界の外側や共通の親へ state を置く必要があるか検討します。

Suspense を使う判断

lazy によるコード分割は、Suspense を使う分かりやすい入口です。データ取得については、React と統合されたフレームワークや Suspense 対応のライブラリが提供する方式に従います。Effect で始めた通信を Suspense へ後から接続するものではありません。

まずは、待機表示が必要な UI のまとまりを特定し、その読み込み方法が Suspense をサポートしているかを確認しましょう。

Sources