T-CREATOR

React 18のSuspense完全対応ガイド:並列レンダリング時代の新常識

React 18のSuspense完全対応ガイド:並列レンダリング時代の新常識

React 18でついに本格実装されたSuspenseは、フロントエンド開発における非同期UIの扱いを大きく変える存在です。

とくに Concurrent Rendering(並列レンダリング) と組み合わせることで、ユーザー体験を損なうことなくデータ読み込みを含むUIを柔軟に設計できるようになりました。

本記事では、React 18のSuspenseに関する全体像と活用方法を、サンプルコードと公式ドキュメントを交えながら丁寧に解説いたします。

Suspenseの役割と進化

React 18以前でもReact.lazy()と組み合わせてコード分割に使うことは可能でした。

しかしReact 18からは データフェッチサーバーサイドレンダリング(SSR) にも活用できるよう拡張され、並列レンダリングと組み合わせた実用的な非同期UI構築が実現します。

バージョンSuspenseの用途備考
React 16.6React.lazy()によるコード分割クライアント限定
React 18データフェッチ、SSRまで対応サーバー・クライアント両方

公式ドキュメント:React Suspense

Suspenseの基本構文と使い方

最も基本的な使い方はReact.lazy()と組み合わせて、遅延ロードされるコンポーネントの読み込み中にFallbackを表示する方法です。

tsximport React, { Suspense } from 'react';

const LazyComponent = React.lazy(() => import('./MyComponent'));

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

上記のように、fallbackにローディング中のプレースホルダーUIを設定することで、スムーズな体験を提供できます。

データフェッチとの統合:use()フックとServer Component

React 18ではuse()フックとSuspenseを組み合わせることで、データフェッチの非同期処理も同様に扱えるようになりました。

Server Componentと組み合わせた形での使用が推奨されます。

tsx// app/page.tsx
import { Suspense } from 'react';

function PostList() {
  const posts = use(fetch('https://jsonplaceholder.typicode.com/posts').then(res => res.json()));
  return (
    <ul>
      {posts.map((post) => <li key={post.id}>{post.title}</li>)}
    </ul>
  );
}

export default function Page() {
  return (
    <Suspense fallback={<div>投稿を取得中...</div>}>
      <PostList />
    </Suspense>
  );
}

このようにuse()フックを通してPromiseを直接扱うことで、非同期の状態をSuspenseで待たせることができます。

公式ドキュメント:React use()

並列レンダリングとTransition APIの活用

React 18の最大の特徴は並列レンダリング(Concurrent Rendering)です。
startTransition()を使うことで、低優先度のUI更新をユーザー操作とは切り離して扱うことが可能になります。

tsximport { useState, startTransition } from 'react';

function SearchInput({ onSearch }) {
  const [input, setInput] = useState('');

  const handleChange = (e) => {
    const value = e.target.value;
    setInput(value);

    startTransition(() => {
      onSearch(value);
    });
  };

  return <input value={input} onChange={handleChange} />;
}

検索のように頻繁な再描画が発生するケースでは、startTransition()を用いることでUXが大きく改善されます。

公式ガイド:React startTransition

Suspenseのネスト構造と複数待機UIの設計

Suspenseは入れ子構造で使用することで、個別の読み込み状態に応じて異なるfallbackを提供できます。

tsxfunction App() {
  return (
    <Suspense fallback={<div>ページ全体を読み込み中...</div>}>
      <Header />
      <Suspense fallback={<div>コンテンツを読み込み中...</div>}>
        <MainContent />
      </Suspense>
      <Footer />
    </Suspense>
  );
}

ネストされたSuspenseそれぞれの非同期処理が完了するタイミングで段階的に描画されるため、体感速度が向上します。

Suspenseでのエラーハンドリング:ErrorBoundaryとの併用

SuspenseではPromiseの失敗による例外をErrorBoundaryで補足できます。

tsxclass MyErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <div>エラーが発生しました</div>;
    }
    return this.props.children;
  }
}
tsxfunction App() {
  return (
    <MyErrorBoundary>
      <Suspense fallback={<div>Loading...</div>}>
        <ComponentWithError />
      </Suspense>
    </MyErrorBoundary>
  );
}

こうすることで、非同期フェッチやモジュール読み込み失敗時のフォールバックUIを明示的に表示できます。

エラー処理の公式ガイド:Error Boundaries

React 18でのSuspenseとStreaming SSRの統合

React 18ではサーバー側で非同期処理を待ちながら段階的にHTMLをストリーミング出力する機能が追加されています。

tsximport { renderToPipeableStream } from 'react-dom/server';

function handleRequest(req, res) {
  const stream = renderToPipeableStream(<App />, {
    onShellReady() {
      res.statusCode = 200;
      res.setHeader('Content-Type', 'text/html');
      stream.pipe(res);
    },
  });
}

Streaming SSRでは、Suspenseが途中で止める場所を制御し、ページ全体を一気に表示せず段階的に描画できます。

詳細はこちら:React Server Rendering

React 18とSuspenseのベストプラクティス

React 18でSuspenseを利用する際のベストプラクティスをまとめます。

項目内容
フォールバックUI適切な場所に、適切な粒度で設定する
エラーハンドリングErrorBoundaryとの併用を推奨
ネスト構造の活用スケルトンローディングと段階的描画に有効
Streaming SSRユーザーに早く表示される体験を提供できる
非同期ロジッククライアント側ではuseEffectで処理を分離

よくある疑問とその回答

Q. Suspenseはクライアントでも使えますか?

はい。従来どおりReact.lazy()によるコード分割では使えますが、データフェッチのような用途にはサーバー環境が適しています。

Q. API呼び出しをuse()で行うには?

use()はサーバーコンポーネント内でPromiseの解決を待つために使います。クライアント側では使用できません。

Q. ReduxやSWRとの違いは?

SuspenseはあくまでUIの非同期待機ロジックであり、状態管理ライブラリと役割が異なります。組み合わせて使うのが理想です。

まとめ:SuspenseはReactの未来を支える基盤

React 18におけるSuspenseは、「非同期処理の待機をUIで表現する」ための汎用的な仕組みとして再定義されました。

並列レンダリング、Streaming SSR、Server Componentと組み合わせることで、これまで実現が難しかった待たせないUX、スケーラブルな描画制御、適応型のデータ取得が可能になります。

フロントエンド開発者にとっては避けて通れない知識となるSuspense。今後のReact開発においては、その理解と活用がプロダクト品質を大きく左右する鍵になるでしょう。

記事Article

もっと見る