React.cache
https://ja.react.dev/reference/react/cache#noun-labs-1201738-(2)
データ取得結果や計算結果をキャッシュするように、渡された関数を メモ化 する
このメモ化された関数を呼び出すと、入力された引数を調べて結果が既にキャッシュされているかを確認する
https://ja.react.dev/reference/react/cache#memoized-function-still-runs
このときの比較は、Shallow comparison (===)で行われる
そのため、プリミティブでない場合は同じオブジェクト参照を渡す必要がある
ほとんど無理なので、プリミティブ値を渡すようにするのが良さそう radish-miyazaki.icon
使用上の注意
メモ化された関数では、プリミティブ値を受け取るようにする
React Server Component で利用 することを想定している
キャッシュにアクセスするためには、コンポーネントは同じメモ化された関数を呼び出す必要がある
そのため、コンポーネントの外側で別ファイルとして定義するする のが良い
https://arc.net/l/quote/vilouhmy
code:js
import {cache} from 'react';
import {calculateWeekReport} from './report';
export default cache(calculateWeekReport);
code:jsx
import getWeekReport from './getWeekReport';
export default function Temperature({cityData}) {
const report = getWeekReport(cityData);
// ...
}
使用例
Next.js の Request Memoization を fetch 以外でも実現したい場合に用いられる
e.g. Prisma Client、NextAuth.js
e.g. Prisma Client
⛔ キャッシュが効かないパターン
code:ts
export const getProfileFromScreenName = async ({ screenName }: Props) => {
const profile = await prisma.profile.findUnique({
where: { screenName },
include: { user: true },
});
if (!profile) notFound();
return profile;
};
code:tsx
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);
// ...
}
generateMetadata と Page で異なる参照のオブジェクトを渡しているので、キャッシュが効かない
React.cache#66cdc29f75d04f00009011b8
✅ 回避策
getProfileFromScreenName でプリミティブ値を受け取るようにする
code:ts
export const getProfileFromScreenName = cache(async (screenName: string) => {
const profile = await prisma.profile.findUnique({
where: { screenName },
include: { user: true },
});
if (!profile) notFound();
return profile;
});
呼び出し元
code:tsx
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);
// ...
}
e.g. NextAuth.js
code:ts
export const getServerSession = cache(async () => {
return originalGetServerSession(authOptions);
});
#React