TL;DR
データ取得や計算の結果をキャッシュするために、渡された関数をメモ化する React の API。React Server Component で使うことを想定している。
なぜ必要か
Next.js の Request Memoization は fetch にしか効かない。Prisma Client や NextAuth.js など、fetch 以外の呼び出しを同じようにメモ化したいときに使う。
仕組み
メモ化された関数を呼び出すと、渡された引数を調べ、結果がすでにキャッシュされているかを確かめる。引数の比較は Shallow comparison(===)で行う。そのため、プリミティブでない値を渡すときは、同じオブジェクトの参照を渡す必要がある。
キャッシュを共有するには、コンポーネントどうしが同じメモ化された関数を呼び出す必要がある。そのため、メモ化した関数はコンポーネントの外の別ファイルで定義する。
import { cache } from "react"
import { calculateWeekReport } from "./report"
export default cache(calculateWeekReport)import getWeekReport from "./getWeekReport"
export default function Temperature({ cityData }) {
const report = getWeekReport(cityData)
// ...
}例
次のコードでは、generateMetadata と Page が別々のオブジェクトを引数に渡すので、キャッシュが効かない。
export const getProfileFromScreenName = async ({ screenName }: Props) => {
const profile = await prisma.profile.findUnique({
where: { screenName },
include: { user: true },
})
if (!profile) notFound()
return profile
}export async function generateMetadata({ params }: Props): Promise<Metadata> {
const profile = await getProfileFromScreenName(params)
return { title: `${profile.user.name}さんの投稿一覧 | ${SITE_NAME}` }
}
export default async function Page({ params }: Props) {
const profile = await getProfileFromScreenName(params)
// ...
}関数を cache で包み、プリミティブ値を受け取るようにすると、キャッシュが効く。
export const getProfileFromScreenName = cache(async (screenName: string) => {
const profile = await prisma.profile.findUnique({
where: { screenName },
include: { user: true },
})
if (!profile) notFound()
return profile
})export async function generateMetadata({ params }: Props): Promise<Metadata> {
const profile = await getProfileFromScreenName(params.screenName)
return { title: `${profile.user.name}さんの投稿一覧 | ${SITE_NAME}` }
}
export default async function Page({ params }: Props) {
const profile = await getProfileFromScreenName(params.screenName)
// ...
}NextAuth.js のセッション取得も、同じように包める。
export const getServerSession = cache(async () => {
return originalGetServerSession(authOptions)
})注意点
- メモ化した関数は、プリミティブ値を受け取るようにする
- React Server Component で使う
- コンポーネントの中ではなく、外の別ファイルで定義する。コンポーネントの中で使えるキャッシュには unstable_cache がある
自分の理解
同じオブジェクトの参照を渡し続けるのはほとんど無理なので、プリミティブ値を渡すようにするのがよさそうだ。