use cache
use cache
ディレクティブは、コンポーネントや関数をキャッシュするために指定します。ファイル上部に置くことで、ファイル内のすべてのエクスポートがキャッシュ可能であることを示したり、関数またはコンポーネントの上部にインラインで記述することで、Next.jsに対して返り値をキャッシュして後続のリクエストで再利用可能であることを伝えたりします。これはNext.jsの実験的な機能であり、use client
やuse server
のようなReactのネイティブな機能ではありません。
使用法
next.config.ts
ファイル内のdynamicIO
フラグを使用してuse cache
ディレクティブのサポートを有効にします:
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}
export default nextConfig
次に、ファイル、コンポーネント、または関数レベルでuse cache
ディレクティブを使用できます:
// ファイルレベル
'use cache'
export default async function Page() {
// ...
}
// コンポーネントレベル
export async function MyComponent() {
'use cache'
return <></>
}
// 関数レベル
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
知っておくべきこと
use cache
はNext.jsの実験的な機能であり、use client
やuse server
のようなReactのネイティブな機能ではありません。- キャッシュされた関数に渡されるあらゆるシリアライズ可能な引数(またはprops)および親スコープから読み取るすべてのシリアライズ可能な値は、JSONのような形式に変換され、自動的にキャッシュキーの一部になります。
- 非シリアライズ可能な引数、props、または閉じ込められた値は、キャッシュされた関数内で不透明な参照になります。これらは通過させることはできますが、調べたり変更したりすることはできません。これらの非シリアライズ可能な値はリクエスト時に埋め込まれ、キャッシュキーの一部にはなりません。
- 例えば、キャッシュされた関数は
children
プロップとしてJSXを受け取り、<div>{children}</div>
を返すことができますが、実際のchildren
オブジェクトを内省することはできません。
- 例えば、キャッシュされた関数は
- キャッシュ可能な関数の返り値もシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得できることが保証されます。
use cache
ディレクティブを使用する関数は、副作用を持たない必要があります。例えば、状態を変更したり、DOMを直接操作したり、コードを定期的に実行するタイマーを設定したりしてはいけません。- Partial Prerenderingと一緒に使用される場合、
use cache
を含むセグメントは静的HTMLシェルの一部としてプリレンダリングされます。 use cache
ディレクティブは将来的にdynamicIO
フラグとは別に利用可能になる予定です。unstable_cache
はJSONデータのみをサポートしていますが、use cache
はReactがレンダリングできるシリアライズ可能なデータ、コンポーネントのレンダリング出力を含むすべてをキャッシュできます。
例
use cache
で一部のルートをキャッシュする
ルート全体をプリレンダリングするには、layout
およびpage
ファイルの両方の上部にuse cache
を追加します。これらの各セグメントはアプリケーション内で独立したエントリーポイントとして扱われ、それぞれが独立してキャッシュされます。
- TypeScript
- JavaScript
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
export default function Layout({ children }) {
return <div>{children}</div>
}
page
ファイル内でインポートされネストされているすべてのコンポーネントはpage
のキャッシュ動作を継承します。
- TypeScript
- JavaScript
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
async function Users() {
const users = await fetch('/api/users')
// ユーザーをループする
}
export default function Page() {
return (
<main>
<Users />
</main>
)
}
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
async function Users() {
const users = await fetch('/api/users')
// ユーザーをループする
}
export default function Page() {
return (
<main>
<Users />
</main>
)
}
これは以前に
export const dynamic = "force-static"
オプションを使用していたアプリケーションに推奨される設定であり、ルート全体がプリレンダリングされることを保証します。
use cache
でコンポーネントの出力をキャッシュする
コンポーネントレベルでuse cache
を使用して、そのコンポーネント内で行われるデータフェッチや計算をキャッシュすることができます。アプリケーション全体でコンポーネントを再利用する場合、propsの構造が同じである限り同じキャッシュエントリを共有することができます。
propsはシリアライズされ、キャッシュキーの一部を形成し、シリアル化されたpropsが各インスタンスで同じ値を生成する限りキャッシュエントリが再利用されます。
export async function Bookings({ type = 'haircut' }: BookingsProps) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
interface BookingsProps {
type: string
}
export async function Bookings({ type = 'haircut' }) {
'use cache'
async function getBookingsData() {
const data = await fetch(`/api/bookings?type=${encodeURIComponent(type)}`)
return data
}
return //...
}
use cache
で関数の出力をキャッシュする
非同期関数にはuse cache
を追加できるため、コンポーネントやルートのみをキャッシュすることに限定されません。ネットワークリクエストやデータベースクエリをキャッシュしたり、非常に遅い計算を行ったりしたい場合があります。この種の作業を含む関数にuse cache
を追加することで、その関数がキャッシュ可能となり、再利用されるときには同じキャッシュエントリを共有します。
- TypeScript
- JavaScript
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
export async function getData() {
'use cache'
const data = await fetch('/api/data')
return data
}
再検証
デフォルトでは、Next.jsはuse cache
ディレクティブを使用するときに**15分の再検証期間**を設定します。Next.jsは事実上無限の有効期限を設定するため、頻繁に更新される必要がないコンテンツに適しています。
頻繁に変更されることを期待していないコンテンツについて、この再検証期間は便利ですが、cacheLife
およびcacheTag
APIを使用してキャッシュの動作を構成することもできます:
これらのAPIはクライアントとサーバーのキャッシングレイヤー全体で統合されており、キャッシングセマンティクスを1か所で設定し、どこにでも適用できます。
詳細については、cacheLife
とcacheTag
のドキュメントをご覧ください。
インターリービング
キャッシュ可能な関数に非シリアライズ可能な引数を渡す必要がある場合は、それをchildren
として渡すことができます。これにより、children
参照を変更してもキャッシュエントリには影響を与えないようになります。
- TypeScript
export default async function Page() {
const uncachedData = await getData()
return (
<CacheComponent>
<DynamicComponent data={uncachedData} />
</CacheComponent>
)
}
async function CacheComponent({ children }) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}
サーバーアクションをキャッシュされたコンポーネントを通してクライアントコンポーネントに渡すこともできますが、それをキャッシュ可能な関数内で呼び出すことはありません。
- TypeScript
- JavaScript
import ClientComponent from './ClientComponent';
export default async function Page() {
const performUpdate = async () => {
"use server"
// サーバー側での更新を実行する
await db.update(...);
};
return <CacheComponent performUpdate={performUpdate} />;
}
async function CachedComponent({ performUpdate }) {
"use cache"
// ここでperformUpdateを呼び出さないでください
return <ClientComponent action={performUpdate} />;
}
import ClientComponent from './ClientComponent';
export default async function Page() {
const performUpdate = async () => {
"use server"
// サーバー側での更新を実行する
await db.update(...);
};
return <CacheComponent performUpdate={performUpdate} />;
}
async function CachedComponent({ performUpdate }) {
"use cache"
// ここでperformUpdateを呼び出さないでください
return <ClientComponent action={performUpdate} />;
}
- TypeScript
- JavaScript
'use client'
export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}
'use client'
export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}