Next.js × useSWRで手軽にデータフェッチ
こんにちは。大壁です。
今回はNext.jsとuseSWRを使って、安全で効率的なデータフェッチを実装していきます。
僕自身も手を動かしながら進めるハンズオン形式です。
1. はじめに
useSWRはVercelが開発しているReact Hooksライブラリで、
キャッシュ・再検証・リアルタイム更新 を簡単に実装できます。
useSWRは何回か使ったことありましたが、Vercelのライブラリなのは初めて知りました。
この記事のゴール:
- Next.js に useSWR を導入
- API Route からデータを取得
- 危険な useEffect + useState のデータフェッチから卒業
では、進めていきましょう
2. なぜuseEffectでのデータフェッチは危険なのか?
まず、なぜuseEffectでのデータフェッチが危険なのかについて、おさらいしましょう。
僕自身も詳しく理解していなかったので、改めて学んでみます。
React初学者がやりがちなパターンは以下です。(今回はchatGPTにまとめてもらいました。)
今回は問題点を分かりやすくするために敢えてstate
を依存配列に入れています。
import { useEffect, useState } from 'react';
export default function BadExample() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/users')
.then(res => res.json())
.then(data => {
setUsers(data);
setLoading(false);
});
}, [users]);
if (loading) return <div>読み込み中...</div>;
return <pre>{JSON.stringify(users, null, 2)}</pre>;
}
問題点
1. 無限ループの危険
依存配列の指定をミスると、setState → 再レンダー → useEffect再実行のループに。
2. 同時リクエストの競合
古いリクエストが後から返ってきて最新データを上書きする可能性。
3. キャッシュがない
ページを行き来するたびに毎回APIアクセス。
4. エラー処理・リトライの実装負担
すべて手作業で書かないといけない。
3. useSWRを使うメリット
useSWRを使うと、これらの問題を解決できます。
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(res => res.json());
export default function GoodExample() {
const { data, error, isLoading } = useSWR('/api/users', fetcher);
if (isLoading) return <div>読み込み中...</div>;
if (error) return <div>エラーが発生しました</div>;
return (
<ul>
{data.map((user: { id: number; name: string }) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
メリット
- 無限ループ防止:内部で依存関係を管理
- 最新データのみ反映:古いレスポンスは自動で破棄
- キャッシュ内蔵:ページ戻り時も即表示
- エラー・ローディング管理:変数1つで判定
- リアルタイム更新簡単:mutate() で即再取得、refreshIntervalで自動更新
こう比べてみると使わない手はないですね
4. ハンズオン — Next.jsで実装
では、次にハンズオンで解説してきます。
プロジェクト作成
npx create-next-app@latest nextjs-swr-demo
cd nextjs-swr-demo
npm install swr
API Route作成
/src/app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
return NextResponse.json([
{ id: 1, name: 'オカルン', description: 'オカルトマニアの男子高生。幽霊は信じてないけど、宇宙人は信じている。ターボババアにかけられた呪いを解くべく追いかけっこをするはめに。' },
{ id: 2, name: 'モモ', description: '霊媒師の家系の女子高生。硬派な男性がタイプ。宇宙人は信じてないけど、幽霊は信じている。セルポ星人にさらわれ、秘めた超能力に目覚める。' },
{ id: 3, name: 'アイラ', description: '校内の人気美少女。“あるもの”を拾ったことで能力が開花。超能力を持つモモを悪魔と勘違いする。' },
{ id: 4, name: 'ジジ', description: 'モモの幼馴染で初恋相手。イケメンで背が高いが、発言・行動がチャラい。 引っ越し先での怪異のため、星子を頼ってやってきた。' },
]);
}
useSWRで取得
/src/app/page.tsx
'use client';
import useSWR from 'swr';
const fetcher = (url: string) => fetch(url).then(res => res.json());
export default function Home() {
const { data, error, isLoading } = useSWR('/api/users', fetcher);
if (isLoading) return <div>読み込み中...</div>;
if (error) return <div>エラーが発生しました</div>;
return (
<main className="container mx-auto p-4">
<h1 className="text-2xl font-bold mb-4">ユーザー 一覧</h1>
<ul className="bg-gray-100 shadow-md rounded-lg p-4">
{data.map((user: { id: number; name: string; description: string }) => (
<li key={user.id} className="border-b border-gray-200 py-2">
<h2 className="font-bold mb-2">{user.name}</h2>
<p className="text-gray-600">{user.description}</p>
</li>
))}
</ul>
</main>
);
}
こちらで簡単に取得して表示することができます。
また、refreshIntervalで5秒後に自動更新させることも可能です。
const { data, error, isLoading } = useSWR('/api/users', fetcher, {
refreshInterval: 5000, // 5秒ごとに再取得
});
6. まとめ
まとめになります。
- useEffectでのデータフェッチは依存配列や競合処理のミスで危険
- useSWRはキャッシュ・再検証・エラー処理が内蔵され安全
- 簡単なコードで安定したデータ取得ができる
改めて学ぶと結構簡単だったので、僕も今後の実装でも取り入れてみようと思います。
それでは、また👋
Discussion