Server Actions と revalidate の関係を追う
Server Actions の実行時、revalidateTag を呼んだときだけ再描画が走る。
これについて、裏側で何が起きてるのかを知りたい。
とりあえず動作の実験。
- APIからは呼び出すたびに日時を返す
- APIはrevalidate:0にしてキャッシュ無効で呼ぶ
- Server Actions では特に何もしない
export default async function Page() {
async function action() {
"use server";
// do nothing
}
const res = await fetch("http://localhost:3000/api", {
next: { tags: ["mytag"], revalidate: 0 },
});
const { date } = await res.json();
return (
<form action={action}>
<p>{date}</p>
<button type="submit">Submit</button>
</form>
);
}
結果→ServerActionsを実行しても何も起きない
今度は revalidateTag を呼んでみる
import { revalidateTag } from "next/cache";
export default async function Page() {
async function action() {
"use server";
revalidateTag("mytag");
}
const res = await fetch("http://localhost:3000/api", {
next: { tags: ["mytag"], revalidate: 0 },
});
const { date } = await res.json();
return (
<form action={action}>
<p>{date}</p>
<button type="submit">Submit</button>
</form>
);
}
結果 → Server Actions を呼ぶたびに再描画される
レスポンスにも違いがある。
※Firefoxのdevtoolsで見れる
revalidateTagなし
revalidateTagあり
revalidateTagのコードを見ると、 store.pathWasRevalidated
に値を設定している。
store は staticGenerationAsyncStorage が入る。
staticGenerationAsyncStorage は、Request Scope で処理される AsyncLocalStorage
pathWasRevalidated
を見てる箇所を探すと、handleAction() の中に行き着く
handleAction() は Server Actions を処理してる関数
!staticGenerationStore.pathWasRevalidated
なので、skipFlight は 「revalidateを行っていなかったら」trueになる。
generateFlight
のコードはこのへん
このとき、タグの値が何かはまったく関係が無い。
「revalidateTagを実行した」という時点で pathWasRevalidated
に値は入る。
つまり、最初のサンプルコードで次のように全然関係ないタグをrevalidateしてみると...
import { revalidateTag } from "next/cache";
export default async function Page() {
async function action() {
"use server";
revalidateTag("ahhhhhh"); // 全然関係ないタグ
}
const res = await fetch("http://localhost:3000/api", {
next: { tags: ["mytag"], revalidate: 0 },
});
const { date } = await res.json();
return (
<form action={action}>
<p>{date}</p>
<button type="submit">Submit</button>
</form>
);
}
ふつうに再描画されるのがわかる。
ちなみに revalidatePath は、実質 revalidateTag なので同じ動きになる
結論
- revalidateTag (revalidatePath) を呼ぶと、ページの再描画が実行されレスポンスに含まれる
- タグやパスが現在のページと関連しているかは関係が無い
なおこれは2023/07/13現在でのcanaryバージョンでのコードをベースにした確認。
将来的に挙動が変わる可能性もある。
部分的な描画など、サブツリーのみに対応したレンダリングは可能か?
現状 // TODO: only revalidate if the path matches
の記述があり、少なくともパスが一致した場合のみ再描画させる、といったことはいずれやりたいみたい
正式版の記事にしようかと思ったけど、α機能かつTODOコメントもある状態ということもあり、内容がわりと短期間で信頼できないものになりそうなので、このスクラップに留めておく