SWRはReact用のデータ取得ライブラリです。同じキーのデータをキャッシュし、表示後にも再取得して新しい値へ更新できます。まずは一覧を1つ取得する場面だけを扱います。
今回やること
npm install swr
useSWRでGET /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>
);
}
初回はisLoadingがtrueです。データが表示された後にフォーカス復帰などで再検証される時は、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がありません。isLoading、error、空配列を先に分けてから、data.mapを呼びます。
練習
/api/postsを取得するPostListを作り、読み込み・エラー・投稿が0件・成功の4状態を表示してください。投稿作成に成功した後、どのキーへmutate()を呼べば一覧が更新されるかも書いてみましょう。