SWR入門:Reactで取得・読み込み・エラー・再検証を扱う

中級 | 12分 で読める | 2025.12.02

公式ドキュメント

SWRはReact用のデータ取得ライブラリです。同じキーのデータをキャッシュし、表示後にも再取得して新しい値へ更新できます。まずは一覧を1つ取得する場面だけを扱います。

今回やること

npm install swr
  • useSWRGET /api/usersを取得する
  • 読み込み中・エラー・空・成功を分けて表示する
  • mutateで手動の再取得をする
  • 再検証中の表示を加える

fetcherを用意する

fetcherはURLを受け取ってJSONを返す関数です。HTTPエラーを成功として扱わないよう、res.okを確認します。

export async function fetcher<T>(url: string): Promise<T> {
  const res = await fetch(url);

  if (!res.ok) {
    throw new Error(`データの取得に失敗しました (${res.status})`);
  }

  return res.json() as Promise<T>;
}

この例は同じサイト内のAPIを前提にしています。認証情報をブラウザのストレージへ独自に保存する処理は、ここへ足しません。認証方式はアプリの既存設計に従ってください。

最小のコンポーネント

import useSWR from "swr";
import { fetcher } from "./fetcher";

type User = { id: string; name: string };

export function UserList() {
  const { data, error, isLoading, isValidating, mutate } =
    useSWR<User[]>("/api/users", fetcher);

  if (isLoading) return <p>読み込み中...</p>;
  if (error) return <p role="alert">{error.message}</p>;
  if (!data || data.length === 0) return <p>利用者はいません。</p>;

  return (
    <section aria-busy={isValidating}>
      <button type="button" onClick={() => mutate()} disabled={isValidating}>
        {isValidating ? "更新中..." : "再取得"}
      </button>
      <ul>
        {data.map((user) => <li key={user.id}>{user.name}</li>)}
      </ul>
    </section>
  );
}

初回はisLoadingtrueです。データが表示された後にフォーカス復帰などで再検証される時は、isValidatingを使うと、一覧を消さずに更新中だけ伝えられます。

条件がそろうまで取得しない

IDが必要なAPIは、IDがない間はキーにnullを渡します。

function UserProfile({ userId }: { userId?: string }) {
  const { data, error } = useSWR<User>(
    userId ? `/api/users/${userId}` : null,
    fetcher,
  );

  if (!userId) return <p>利用者を選択してください。</p>;
  if (error) return <p role="alert">取得できませんでした。</p>;
  if (!data) return <p>読み込み中...</p>;
  return <h1>{data.name}</h1>;
}

nullは「失敗」ではなく「今は取得しない」という意味です。undefinedのIDをURLへ連結して、意図しないリクエストを出さないようにします。

mutateで更新後の表示をそろえる

作成・更新・削除を別の処理で行った後は、同じキーのmutate()を呼び、一覧を再検証できます。

import { useSWRConfig } from "swr";

function RenameButton() {
  const { mutate } = useSWRConfig();

  async function rename() {
    const res = await fetch("/api/users/42", {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ name: "Ada" }),
    });

    if (!res.ok) throw new Error("更新に失敗しました");
    await mutate("/api/users"); // 一覧のキーを再取得する
  }

  return <button onClick={rename}>名前を更新</button>;
}

更新リクエスト側でもres.okを確認し、失敗した時に成功したように表示しないことが必要です。最初はサーバーの結果を取り直す形から始め、楽観的更新は競合やロールバックを説明できるようになってから導入します。

成功確認

開発者ツールのNetworkで、最初に/api/usersへ1回リクエストが出ることを確認します。次に再取得ボタンを押し、一覧を消さずに「更新中…」が表示され、成功時は最新のデータに変わることを確認してください。

APIを一時的に失敗させられる環境なら、エラー文が表示され、空の一覧を成功として見せないことも確認します。

よくあるつまずき

キーが毎回変わる

同じデータには同じキーを使います。レンダーごとに異なる時刻やランダム値をキーへ含めると、キャッシュを再利用できません。

dataがない時にすぐmapする

初回はdataがありません。isLoadingerror、空配列を先に分けてから、data.mapを呼びます。

練習

/api/postsを取得するPostListを作り、読み込み・エラー・投稿が0件・成功の4状態を表示してください。投稿作成に成功した後、どのキーへmutate()を呼べば一覧が更新されるかも書いてみましょう。

次のステップ

参考リソース

← 一覧に戻る
PR
PR
PR
PR