Tags:
Next.js
Next.js で revalidatePath するたびに別のページがローディング表示になる事象の原因
Next.js(App Router)で、どこかのページで revalidatePath
するたびに別のページの Suspense
で囲んでいる部分がローディング表示になる事象が起きたので、どうしてそうなるのか詳しく調べた。
環境
- Next.js 14.2.4
本文
結論を言うと、fetch に cache: "no-cache"
オプションを付ける(あるいはそれに準ずる状態である)とそうなる。
例として下記のような実装を考える。
import Link from "next/link";
import { Suspense } from "react";
export default function Home() {
return (
<div>
<Suspense fallback={<p>Loading...</p>}>
<Content />
</Suspense>
<Suspense fallback={<p>Loading...</p>}>
<NoCacheContent />
</Suspense>
</div>
);
}
async function Content() {
const res = await fetch("http://localhost:3000/api/top", {
next: { revalidate: 10 }
});
const data = await res.json();
return <p>{data.title}</p>;
}
async function NoCacheContent() {
const res = await fetch("http://localhost:3000/api/top", {
cache: "no-cache"
});
const data = await res.json();
return <p>{data.title}</p>;
}
<Content>
コンポーネントのほうは 10 秒ごとに revalidate する。
しかし実際には client-side Router Cache があるのでそう頻繁に revalidate されることはない。
client-side Router Cache が切れるたびに revalidate するような挙動を期待している。
一方 <NoCacheContent>
は cache: "no-cache"
オプションをつけている。
レスポンスはキャッシュされず、client-side Router Cache が切れるたびに最新のデータを取り直す。
これら二つのコンポーネントは以下のような挙動となる。
<Content>
コンポーネント- ローディング UI は最初の fetch のときと、このコンポーネントが表示されているページに対して
revalidatePath
を実行したときに表示される。 - client-side Router Cache が切れるたびにキャッシュデータを更新するが、画面への表示はキャッシュデータが先行する。つまり、前回 client-side Router Cache が切れた瞬間のデータを、次に client-side Router Cache が切れたときに表示する……という感じでデータの鮮度がワンテンポ遅れる。
- ローディング UI は最初の fetch のときと、このコンポーネントが表示されているページに対して
<NoCacheContent>
コンポーネント- ローディング UI は最初の fetch のときと、client-side Router Cache が切れたときと、このコンポーネントが表示されているページに対して
revalidatePath
を実行したときに表示される。revalidatePath
の現在の挙動はすべてのルートの client-side Router Cache を削除するため、このコンポーネントでは別のページに対してrevalidatePath
を実行したときもローディング UI が表示されてしまう。
- client- side Router Cache が切れたときに fetch したデータを表示するため、データの鮮度がワンテンポ遅れるということはない。
- ローディング UI は最初の fetch のときと、client-side Router Cache が切れたときと、このコンポーネントが表示されているページに対して
client-side Router Cache は 30 秒に 1 回は切れるため、<NoCacheContent>
コンポーネント側は 30 秒に 1 回以上ローディング UI を表示することになる。
実際のところ、この挙動は非常に鬱陶しい。
cache: "no-cache"
オプションをつけなければならないのなら Suspense
を使ってサーバーサイドで取得するのはやめたほうがいい。
以上