できない.dev

Next.js の revalidatePath / revalidateTag が効かず古いデータが残る

App Router のキャッシュは fetch / Route / Router Cache の多層構造。
revalidatePath / revalidateTag は fetch と Route Cache を破棄するが、クライアント側 Router Cache や呼び出し場所の制約を踏まえないと反映されない。

#nextjs#revalidate#cache#app-router#isr

公開:

要約

revalidatePath / revalidateTag を呼んだのにページが更新されない場合、原因は App Router のキャッシュ階層を 1 つだけ叩いているケースが多い。
fetch にタグが付いているか、Server Action から呼ばれているか、クライアント Router Cache をどう更新するかの 3 点を順に確認する。

よくある原因

  1. タグ未指定: fetch(url) のように next: { tags: [...] } を渡していないと、revalidateTag('posts') を呼んでも対応する fetch は破棄されない。
  2. Client Component から呼んでいる: 両 API は Server Action / Route Handler 限定で、Client Component から直接 import しても実行できない。
  3. Pages Router で使っている: revalidatePath は App Router 専用。
    Pages Router の ISR は res.revalidate('/path') を使う。
  4. クライアント Router Cache: サーバ側のキャッシュは破棄できても、クライアントが保持する Router Cache に古いデータが残り、ナビゲーションでは更新されないことがある。

解決策

1. fetch にタグを付ける

// app/lib/posts.ts
export async function getPosts() {
  const res = await fetch("https://api.example.com/posts", {
    next: { tags: ["posts"] },
  });
  return res.json();
}

その上で Server Action 側で呼ぶ。

"use server";
import { revalidateTag } from "next/cache";
 
export async function createPost(formData: FormData) {
  // ... 書き込み
  revalidateTag("posts");
}

revalidateTag 公式リファレンス のとおり、タグが一致した fetch だけが破棄される。

2. Server Action / Route Handler 内で呼ぶ

'use client' のコンポーネントから直接 revalidatePath を import しても動かない。
必ず 'use server' 関数の中で呼び、フォームの action などから起動する。

3. Pages Router は別 API

// pages/api/revalidate.ts
export default async function handler(req, res) {
  await res.revalidate("/blog/" + req.query.slug);
  res.json({ revalidated: true });
}

App Router 用の API は pages/ 配下では使えない。

4. クライアント側の即時反映

"use client";
import { useRouter } from "next/navigation";
 
export function RefreshButton() {
  const router = useRouter();
  return <button onClick={() => router.refresh()}>更新</button>;
}

router.refresh() は現在のルートを再 fetch して Router Cache を更新する。
App Router のキャッシュ階層は Caching in Next.js 公式ガイド で全体像が掴める。

この記事は役立ちましたか?