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:
他にも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
を使用し、キャッシュすることを強制できます。