Reactアプリケーションにおけるデータフェッチングは、ユーザーエクスペリエンスとパフォーマンスに直結する重要な要素です。SWR(Stale-While-Revalidate)は、この課題を効率的に解決するNext.jsチーム開発のライブラリです。
1. SWRとは
基本概念
SWRは「Stale-While-Revalidate」HTTPキャッシング戦略に基づくReact
Hooksライブラリです。この戦略は以下の3ステップで動作します:
- キャッシュからデータを返す(Stale)
- バックグラウンドでフェッチリクエストを送信(Revalidate)
- 最新データで更新
主要な特徴
- 軽量かつ再利用可能なデータ取得
- 内蔵キャッシングとリクエスト重複排除
- リアルタイムエクスペリエンス対応
- トランスポート・プロトコル非依存
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アプリケーション開発における強力なツールとなるでしょう。