Next.js 15のuse cahceディレクティブについて、紹介し、今までのキャッシュと何が違うかについて、解説します。
use cahce は、use server や use clientのように Next.js 15 で導入された新しいディレクティブです。
use server や use clientは、React のネイティブの機能であり、use cacheは Next.js の機能になります。
use cahce は、コンポーネントや関数をキャッシュするために使用されます。
ファイル自体をキャッシュする場合は、ファイルの先頭に、関数やコンポーネント単位でキャッシュする場合は、インラインに記述します。
// File level
"use cache";
export default async function Page() {
// ...
}
// Component level
export async function MyComponent() {
"use cache";
return <></>;
}
// Function level
export async function getData() {
"use cache";
const data = await fetch("/api/data");
return data;
}
2024 年 11 月現在では experimental な機能であり、下記のようにnext.config.jsに設定を追加することで有効になります。
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
};
export default nextConfig;
dynamicIOフラグを有効にした状態で、fetchをしたり、Async Request APIを使用すると、静的解析され、エラーが発生します。
export async function Page() {
const data = await fetch("/api/data");
return <div>{data}</div>;
}
[ Server ] Error: Route "/": A component accessed data, headers, params, searchParams, or a short-lived cache without a Suspense boundary nor a "use cache" above it. We don't have the exact line number added to error messages yet but you can see which component in the stack below. See more info:
ですので、もし、動的なデータを取得する場合は、Suspenseの使用が強制されます。
export async function DynamicComponent() {
const data = await fetch("/api/data");
return <div>{data}</div>;
}
export async function Page() {
const data = await fetch("/api/data");
const todos = await data.json();
return (
<Suspense fallback={<>...Loading</>}>
<DynamicComponent />
</Suspense>
);
}
fetchしてくるデータをキャッシュしたい場合は、use cacheを使用します。
export async function CacheComponent() {
"use cache";
const data = await fetch("/api/data");
return <div>{data}</div>;
}
export async function Page() {
return <CahceComponent />;
}
また、use cache内で,Async Request APIを使用しても、下記エラーが発生します。
"use cache";
import { cookies } from "next/headers";
export default async function Page() {
const data = await fetch("/api/data");
const todos = (await data.json()) as { id: number; title: string }[];
const cookieStore = await cookies();
cookieStore.get("token");
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
}
[ Server ] Error: Route / used "cookies" inside "use cache". Accessing Dynamic data sources inside a cache scope is not supported. If you need this data inside a cached function use "cookies" outside of the cached function and pass the required dynamic data in as an argument. See more info here: https://nextjs.org/docs/messages/next-request-in-use-cache
他にもuse cacheは revalidate できたり、tag を指定できたりするのですが、一旦、use cacheの説明はここまでにして、今までのキャッシュとの違いを解説します。
use cacheは、関数の場合、引数、コンポーネントの場合は、props がキャッシュキーとして暗黙的に生成されます。
そのため、キャッシュキーの管理が不要になります。
例えば、unstable_cacheの場合のキャッシュキーは以下のようになります。
const getCachedUser = unstable_cache(
async () => {
return { id: params.userId };
},
[params.userId] // キャッシュキー
);
unstable_cacheの場合でも、第一引数の関数自体と、引数がキャッシュとして自動で生成されますが、関数に引数がない場合は、キャッシュキーを指定する必要があります。
またそれ以外の場合でも、自由に任意にキャッシュキーを追加する事ができます。
use cacheの場合は、キャッシュキーが不要なだけでなく、任意でキーを追加する事もできません。
その代わり、シリアライザブルな引数、props でないといけないので、その点は注意が必要です。
また、返り値もシリアライザブルでないといけないです。(JSX を除く)
Suspenseは React の core チームなどからは、Suspense Boundaryと呼ばれていました。
また、use clientも境界線として、意味合いがあります。
use cacheも、Suspenseやuse clientと同じようにキャッシュする領域を境界線として定義できます。
これは今までのソフトウェアのアーキテクチャにはなかったのではないかと思います。
また、境界線とはちょっと違いますが、use serverとも似ていると言われてします。
use cacheもインラインで宣言でき、server action のように呼び出せるからです。
ただ、server action と違い呼び出されるのはcache(function or component)です。
先ほど、dynamicIOフラグを有効にした状態で、async componentをuse cacheもSuspenseもなしで、レンダリングすると、エラーが発生しました。
これは、動的なコンポーネントであれば、Suspenseの使用を強制でき、そうでなければ、use cacheを使用し、キャッシュすることを強制できます。