React SuspenseでUIを設計する際に避けたいアンチパターンと解決策
React 18以降で正式サポートされたSuspenseは、非同期処理とUIの分離を簡潔に行える強力な仕組みです。
しかし、その使い方を誤ると、UXの劣化や保守性の低下、パフォーマンスの問題を引き起こすこともあります。
本記事では、React Suspenseの導入時に避けるべき代表的なアンチパターンを5つ厳選し、具体的なコード例とともにわかりやすく解説いたします。
Suspenseの基本がわかっている方はもちろん、これから導入を検討している方にも役立つ内容です。
初期読み込みにSuspenseを多用しすぎる設計
非同期UIの入り口として便利なSuspenseですが、最初の表示において多用しすぎるとUXが崩壊する恐れがあります。
問題点:ローディング地獄の発生
tsx<Suspense fallback={<Spinner />}>
  <UserProfile />
</Suspense>
<Suspense fallback={<Spinner />}>
  <UserPosts />
</Suspense>
<Suspense fallback={<Spinner />}>
  <UserFriends />
</Suspense>
一見スマートに見えるこの構成ですが、個別にデータフェッチが走り、それぞれバラバラにローディングスピナーが表示されます。
この結果、以下のような問題が起こります。
| 問題 | 内容 | 
|---|---|
| ローディングの乱立 | UIにスピナーが複数同時に表示され、視認性が悪化 | 
| 初期表示の遅延 | 最も遅いコンポーネントが描画完了するまで待たされる | 
| 一貫性の欠如 | 一部だけ表示・一部だけローディングで体験がブレる | 
解決策:大枠を1つのSuspenseでラップする
tsx<Suspense fallback={<PageSkeleton />}>
  <UserPage />
</Suspense>
補足:公式ドキュメントより
fetch関数を直接使ってリソース管理する
Suspenseの大前提として、「リソースがthrowするPromiseを返す」必要があります。
これを知らずに普通のfetchをそのまま使うと、うまく動作しません。
アンチパターン:Promiseの扱いを明示せずfetchする
tsxconst User = () => {
  const data = fetch('/api/user') // これはSuspenseで待ってくれない
    .then(res => res.json());
  return <div>{data.name}</div>; // dataはPromiseのまま
};
これはただの非同期処理です。Suspenseは関知しません。
解決策:リソースをラップするヘルパー関数を作成する
tsfunction wrapPromise<T>(promise: Promise<T>) {
  let status = 'pending';
  let result: T;
  let suspender = promise.then(
    r => {
      status = 'success';
      result = r;
    },
    e => {
      status = 'error';
      result = e;
    }
  );
  return {
    read() {
      if (status === 'pending') throw suspender;
      if (status === 'error') throw result;
      return result;
    },
  };
}
tsxconst resource = wrapPromise(fetch('/api/user').then(res => res.json()));
function User() {
  const user = resource.read(); // throwされるのでSuspenseで待てる
  return <div>{user.name}</div>;
}
これで初めて、Suspenseに対応した「遅延読み込み」の恩恵が得られます。
エラーハンドリングをSuspenseと混同する
Suspenseは「読み込み待ちの状態」を処理しますが、「エラー状態」は扱いません。
これを混同してしまうと、アプリが意図しない挙動を示します。
アンチパターン:Suspenseでエラーも扱えると誤解
tsx<Suspense fallback={<Loading />}>
  <ComponentThatMightThrow />
</Suspense>
このままでは、非同期処理が失敗して例外が発生しても、何も処理されずクラッシュします。
解決策:ErrorBoundaryを併用する
tsx<ErrorBoundary fallback={<ErrorUI />}>
  <Suspense fallback={<Loading />}>
    <ComponentThatMightThrow />
  </Suspense>
</ErrorBoundary>
ErrorBoundaryを使うことで、エラー発生時もUIで対処できるようになります。
公式リンク:
前半では、Suspense利用時に気をつけるべき3つのアンチパターンを紹介しました。
後半では、実際の開発現場でも特によく見かける以下の2点に焦点を当てて解説を進めてまいります。
- Suspenseのネスト地獄による保守性の低下
 - データ取得ライブラリとの併用ミス(SWRやReact Queryとの誤用)
 
これらの問題に適切に対処することで、コードの可読性・拡張性・パフォーマンスが飛躍的に向上いたします。
Suspenseのネスト地獄による保守性の低下
一見問題なさそうに見えるSuspenseのネストも、数が増えると管理不能なスパゲッティコードになりがちです。
アンチパターン:コンポーネントごとにSuspenseを入れすぎる
tsx<Suspense fallback={<LoadingA />}>
  <ComponentA />
  <Suspense fallback={<LoadingB />}>
    <ComponentB />
    <Suspense fallback={<LoadingC />}>
      <ComponentC />
    </Suspense>
  </Suspense>
</Suspense>
このような構成は、一見効率的に見えて、以下のような問題を引き起こします。
| 問題点 | 内容 | 
|---|---|
| 保守性の悪化 | fallbackやErrorBoundaryの管理が複雑になる | 
| UIの一貫性欠如 | ネストが深くなるとUXが破綻しやすい | 
| 状態追跡の難化 | どの層で何がロード中なのか判別困難 | 
解決策:UI単位で適切にグループ化
まずは大きな単位でローディング状態を包み、必要に応じて粒度を調整する方法がおすすめです。
tsx<Suspense fallback={<GlobalSkeleton />}>
  <DashboardContent />
</Suspense>
さらに、個別にどうしても待機が必要な箇所のみ、ローカルなSuspenseを追加します。
tsxfunction DashboardContent() {
  return (
    <>
      <Profile />
      <Suspense fallback={<ChartSkeleton />}>
        <AnalyticsChart />
      </Suspense>
    </>
  );
}
必要最小限のSuspense構成にすることで、管理が格段に楽になります。
データ取得ライブラリとの併用ミス(SWR / React Query)
React SuspenseとSWR、React Queryなどのクライアントサイドデータ取得ライブラリは非常に相性が良いです。
しかし、設定を誤るとSuspenseが機能しなかったり、予期せぬ挙動が発生することがあります。
アンチパターン:React QueryでSuspenseを有効にし忘れる
tsxconst { data } = useQuery(['user'], fetchUser);
// Suspenseが効かず、undefinedで動く
この場合、useQueryは非同期読み込みの進行中でもundefinedを返すため、Suspenseはトリガーされません。
解決策:suspense: trueを設定する
tsxconst { data } = useQuery(['user'], fetchUser, { suspense: true });
これでuseQueryが「データ未取得状態ではPromiseをthrowする」ようになり、Suspenseのfallbackが機能します。
同様に、SWRでも以下のように対応可能です。
tsxconst { data } = useSWR('/api/user', fetcher, { suspense: true });
公式リンク:
最後に:Suspenseを使いこなすために意識すべきポイントまとめ
以下に、今回紹介した5つのアンチパターンとそれに対するベストプラクティスをまとめます。
| アンチパターン | 問題 | 推奨される対応策 | 
|---|---|---|
| 初期読み込みにSuspenseを多用 | UX崩壊、スピナー乱立 | 1つの大枠でラップし、統一感を持たせる | 
| fetchを直接使用 | Suspenseが効かない | Promiseラッパーを使ってread()で取得 | 
| エラーとSuspense混同 | アプリクラッシュ | ErrorBoundaryを必ず併用 | 
| Suspenseのネスト地獄 | 保守性低下、UI破綻 | 粒度を見直し、最小構成でまとめる | 
| SWR/React Query設定漏れ | Suspenseが動作しない | suspense: trueを明示的に指定 | 
Suspenseは、適切に設計すれば非同期UIを劇的に改善できるツールです。
ですが、Reactチームも明言しているように、「Suspenseは魔法ではなく、構造の上に成り立つツール」です。
その使いどころと設計方針を明確にし、必要な場面にだけ限定的に使うことで、プロダクト全体の品質が向上いたします。
参考リンク
- React 公式 Suspense ドキュメント
 - React 公式 ErrorBoundary ドキュメント
 - TanStack React Query Suspense対応ガイド
 - SWR公式 Suspense
 
今後もReactの非同期レンダリングに関するベストプラクティスを発信していきます。
本記事が、ReactのUI設計における指針となれば幸いです。
articleReact フック完全チートシート:useState から useTransition まで用途別早見表
articleReact 開発環境の作り方:Vite + TypeScript + ESLint + Prettier 完全セットアップ
articleReact とは? 2025 年版の特徴・強み・実務活用を一気に理解する完全解説
articleESLint を Yarn + TypeScript + React でゼロから構築:Flat Config 完全手順(macOS)
article【徹底比較】Preact vs React 2025:バンドル・FPS・メモリ・DX を総合評価
article既存 React プロジェクトを Preact に移行する完全ロードマップ
articleZustand × Suspense:データ取得を直感的に扱う設計パターン
articleReact 18 × Jotai 完全対応ガイド - Suspense との連携方法
articleReact × Suspenseを組み合わせてスケーラブルな非同期UIを実現する方法
articleNext.js × Suspenseを活用した非同期ルーティング体験を実現する方法
articleReact SuspenseでUIを設計する際に避けたいアンチパターンと解決策
articleReact Suspenseでデータフェッチ!fetchでは動かない理由と正しい使い方
articleYarn で社内テンプレート管理:dlx・create-* の私設スキャフォールド術
articleRemix 本番運用チェックリスト:ビルド・監視・バックアップ・脆弱性対応
articlePreact で Hydration mismatch が出る原因と完全解決チェックリスト
articleWeb Components のパッケージ配布戦略:types/CEM(Custom Elements Manifest)/ドキュメント自動
articlePlaywright Debug モード活用:テストが落ちる原因を 5 分で特定する手順
articleVue.js でメモリリーク?watch/effect/イベント登録の落とし穴と検知法
blogiPhone 17シリーズの発表!全モデルiPhone 16から進化したポイントを見やすく整理
blogGoogleストアから訂正案内!Pixel 10ポイント有効期限「1年」表示は誤りだった
blog【2025年8月】Googleストア「ストアポイント」は1年表記はミス?2年ルールとの整合性を検証
blogGoogleストアの注文キャンセルはなぜ起きる?Pixel 10購入前に知るべき注意点
blogPixcel 10シリーズの発表!全モデル Pixcel 9 から進化したポイントを見やすく整理
blogフロントエンドエンジニアの成長戦略:コーチングで最速スキルアップする方法
review今の自分に満足していますか?『持たざる者の逆襲 まだ何者でもない君へ』溝口勇児
reviewついに語られた業界の裏側!『フジテレビの正体』堀江貴文が描くテレビ局の本当の姿
review愛する勇気を持てば人生が変わる!『幸せになる勇気』岸見一郎・古賀史健のアドラー実践編で真の幸福を手に入れる
review週末を変えれば年収も変わる!『世界の一流は「休日」に何をしているのか』越川慎司の一流週末メソッド
review新しい自分に会いに行こう!『自分の変え方』村岡大樹の認知科学コーチングで人生リセット
review科学革命から AI 時代へ!『サピエンス全史 下巻』ユヴァル・ノア・ハラリが予見する人類の未来