SWR完全ガイド2025: React効率的データフェッチングライブラリ

Reactアプリケーションにおけるデータフェッチングは、ユーザーエクスペリエンスとパフォーマンスに直結する重要な要素です。SWR(Stale-While-Revalidate)は、この課題を効率的に解決するNext.jsチーム開発のライブラリです。

1. SWRとは

基本概念

SWRは「Stale-While-Revalidate」HTTPキャッシング戦略に基づくReact
Hooksライブラリです。この戦略は以下の3ステップで動作します:

  1. キャッシュからデータを返す(Stale)
  2. バックグラウンドでフェッチリクエストを送信(Revalidate)
  3. 最新データで更新

主要な特徴

  • 軽量かつ再利用可能なデータ取得
  • 内蔵キャッシングとリクエスト重複排除
  • リアルタイムエクスペリエンス対応
  • トランスポート・プロトコル非依存

2. 基本的な使用方法

インストール

npm install swr
# または
yarn add swr

基本的な実装例

import useSWR from 'swr'

// フェッチャー関数の定義
const fetcher = (url: string) => fetch(url).then(res => res.json())

function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)

  if (error) return <div>読み込みに失敗しました</div>
  if (isLoading) return <div>読み込み中...</div>
  return <div>こんにちは {data.name}さん!</div>
}

TypeScriptでの型安全な実装

interface User {
  id: number
  name: string
  email: string
}

function UserProfile() {
  const { data, error, isLoading } = useSWR<User>('/api/user', fetcher)

  if (error) return <div>エラーが発生しました</div>
  if (isLoading) return <div>読み込み中...</div>

  return (
    <div>
      <h1>{data?.name}</h1>
      <p>{data?.email}</p>
    </div>
  )
}

3. 高度な機能と活用法

条件付きフェッチング

function UserPosts({ userId }: { userId: number | null }) {
  // userIdがnullの場合はフェッチしない
  const { data, error } = useSWR(
    userId ? `/api/users/${userId}/posts` : null,
    fetcher
  )

  return <div>{/* ポスト表示ロジック */}</div>
}

プリフェッチングとキャッシュ制御

import { mutate } from 'swr'

function PostList() {
  const { data: posts } = useSWR('/api/posts', fetcher)

  const handleHover = (postId: string) => {
    // ホバー時に詳細データをプリフェッチ
    mutate(`/api/posts/${postId}`, fetcher(`/api/posts/${postId}`))
  }

  return (
    <div>
      {posts?.map(post => (
        <div key={post.id} onMouseEnter={() => handleHover(post.id)}>
          {post.title}
        </div>
      ))}
    </div>
  )
}

無限スクロール実装

import useSWRInfinite from 'swr/infinite'

function PostFeed() {
  const getKey = (pageIndex: number, previousPageData: any) => {
    if (previousPageData && !previousPageData.length) return null
    return `/api/posts?page=${pageIndex}`
  }

  const { data, error, size, setSize } = useSWRInfinite(getKey, fetcher)

  const posts = data ? data.flat() : []
  const isLoadingMore = data?.[size - 1]?.length === 0

  return (
    <div>
      {posts.map(post => (
        <article key={post.id}>{post.content}</article>
      ))}
      <button
        onClick={() => setSize(size + 1)}
        disabled={isLoadingMore}
      >
        {isLoadingMore ? '読み込み中...' : 'もっと読む'}
      </button>
    </div>
  )
}

4. パフォーマンス最適化

グローバル設定


import { SWRConfig } from 'swr'

function App() {
  return (
    <SWRConfig
      value={{
        refreshInterval: 3000, // 3秒ごとに再検証
        revalidateOnFocus: false, // フォーカス時の再検証を無効
        dedupingInterval: 2000, // 2秒間のリクエスト重複排除
        fetcher: (url: string) => fetch(url).then(res => res.json())
      }}
    >
      <MyApp />
    </SWRConfig>
  )
}

エラーハンドリングとリトライ

function useApiWithRetry<T>(url: string) {
  return useSWR<T>(url, fetcher, {
    onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
      // 404エラーの場合はリトライしない
      if (error.status === 404) return;

      // 最大3回までリトライ
      if (retryCount >= 3) return;

      // 指数バックオフでリトライ
      setTimeout(() => revalidate({ retryCount }), 2 ** retryCount * 1000);
    },
  });
}

5. 2025年のベストプラクティス

React Server ComponentsとSWR

// app/page.tsx (Next.js 13+ App Router)
import { Suspense } from 'react'
import ClientComponent from './client-component'

export default function Page() {
  return (
    <Suspense fallback={<div>読み込み中...</div>}>
      <ClientComponent />
    </Suspense>
  )
}

React QueryとSWRの比較選択

特徴 SWR React Query
学習コストの低さ ✅ 高い ⚠️ 中程度
軽量性 ✅ 軽量 ⚠️ やや重い
高度なキャッシング ⚠️ 基本的 ✅ 豊富
DevTools ⚠️ 限定的 ✅ 充実

推奨選択基準:

  • SWR: シンプルなアプリケーション、Next.js中心の開発
  • React Query: 複雑なキャッシング要件、大規模アプリケーション

6. 実践的な統合例

認証付きAPIとの統合

import useSWR from 'swr';
import { useAuth } from './auth-context';

const authenticatedFetcher = (url: string, token: string) =>
  fetch(url, {
    headers: { Authorization: `Bearer ${token}` },
  }).then(res => res.json());

function useAuthenticatedAPI<T>(endpoint: string) {
  const { token } = useAuth();

  return useSWR<T>(token ? [endpoint, token] : null, ([url, token]) =>
    authenticatedFetcher(url, token)
  );
}

フォームとの連携

function EditProfile() {
  const { data: user, mutate } = useSWR('/api/user', fetcher)
  const [isUpdating, setIsUpdating] = useState(false)

  const handleSubmit = async (formData: FormData) => {
    setIsUpdating(true)

    try {
      // 楽観的更新
      mutate({ ...user, ...formData }, false)

      // サーバーに送信
      const updatedUser = await fetch('/api/user', {
        method: 'PUT',
        body: JSON.stringify(formData)
      }).then(res => res.json())

      // 成功時に再検証
      mutate(updatedUser)
    } catch (error) {
      // エラー時に元のデータに戻す
      mutate(user)
    } finally {
      setIsUpdating(false)
    }
  }

  return <ProfileForm user={user} onSubmit={handleSubmit} />
}

7. トラブルシューティング

よくある問題と解決法

1. 過剰な再フェッチ

// 問題: 不要な再フェッチが発生
const { data } = useSWR('/api/data', fetcher);

// 解決: 適切な設定で制御
const { data } = useSWR('/api/data', fetcher, {
  revalidateOnFocus: false, // フォーカス時の再検証を無効
  revalidateOnReconnect: false, // 再接続時の再検証を無効
  refreshInterval: 0, // 自動更新を無効
});

2. メモリリークの防止

import { useSWRConfig } from 'swr';

function Component() {
  const { cache } = useSWRConfig();

  useEffect(() => {
    return () => {
      // コンポーネントアンマウント時にキャッシュをクリア
      cache.delete('/api/temporary-data');
    };
  }, [cache]);
}

まとめ

SWRは2025年においても、Reactアプリケーションでの効率的なデータフェッチングを実現する優秀なライブラリです。その軽量性とシンプルなAPIは、開発生産性の向上とユーザーエクスペリエンスの最適化を同時に実現します。

特に以下の場面でのSWR活用をお勧めします:

  • リアルタイム性が重要なアプリケーション
  • Next.jsを使用したプロジェクト
  • 学習コストを抑えたいチーム開発
  • 軽量なソリューションを求める場合

適切な設定と活用により、SWRはモダンなWebアプリケーション開発における強力なツールとなるでしょう。