TL;DR
Next.js の 4 種類のキャッシュの 1 つ。同じ URL とオプションを持つ fetch リクエストを自動的にメモ化し、React のコンポーネントツリーの中でデータを再利用する。
なぜ必要か
コンポーネントツリーの複数の場所で同じデータが必要になると、同じ fetch が何度も呼ばれる。Request Memoization が有効なら、どこで fetch を呼んでも、実際のリクエストは 1 回だけで済む。データを親から子へ受け渡さなくても、必要なコンポーネントで直接 fetch を呼べる。
仕組み
Next.js は Fetch API を拡張し、同じ URL とオプションを持つリクエストを自動的にメモ化する(Next.js による fetch の拡張)。名前は Next.js のキャッシュの一つとして並ぶが、実体は React の機能である。
1 回のレンダリングの中では、次の順に動く。
- 最初のリクエストはメモリにないので、キャッシュは
MISSになる - 実際にデータを取得するか Data Cache を参照し、結果をキャッシュに格納する(
SET) - 同じレンダリングの中で同じリクエストが来ると、キャッシュから結果を返す(
HIT) - レンダリングが終わって Route ができあがると、キャッシュはリセットされる
fetch 以外で同じ挙動を実現したい場合は、React.cache を使う。
例
次のコードでは、generateMetadata と Page でクエリパラメータ take の値が異なるため、メモ化が効かない。Data Cache の影響を除くため、cache: "no-store" を指定している。
export async function generateMetadata({ params }: Props): Promise<Metadata> {
// ...
const res = await fetch(`http://localhost:8082/api/categories/${categoryName}?page=1&take=10`, {
cache: "no-store",
})
// ...
}
export default async function Page({ params, searchParams }: Props) {
// ...
const res = await fetch(
// クエリパラメータが generateMetadata と異なる
`http://localhost:8082/categories/${categoryName}?page=${page}&take=15`,
{ cache: "no-store" },
)
// ...
}generateMetadata の take を 15 にそろえると、メモ化が効くようになる。
注意点
- 対象は
fetchのGETリクエストだけである - 対象は React Server Component の中の
fetchである。Route Handler のfetchは、コンポーネントツリーの外にあるので対象にならない - Next.js 15 から Data Cache はデフォルトで無効になったが、Request Memoization は今も有効である
自分の理解
Request Memoization と Data Cache はよく混同してしまう。Data Cache が永続的なのに対し、Request Memoization は 1 回のレンダリングの間しか保持されない。