メインコンテンツまでスキップ

use cache

use cacheディレクティブは、コンポーネントや関数をキャッシュするために指定します。ファイル上部に置くことで、ファイル内のすべてのエクスポートがキャッシュ可能であることを示したり、関数またはコンポーネントの上部にインラインで記述することで、Next.jsに対して返り値をキャッシュして後続のリクエストで再利用可能であることを伝えたりします。これはNext.jsの実験的な機能であり、use clientuse serverのようなReactのネイティブな機能ではありません。

使用法

next.config.tsファイル内のdynamicIOフラグを使用してuse cacheディレクティブのサポートを有効にします:

next.config.ts
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 clientuse 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を追加します。これらの各セグメントはアプリケーション内で独立したエントリーポイントとして扱われ、それぞれが独立してキャッシュされます。

app/layout.tsx
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}

pageファイル内でインポートされネストされているすべてのコンポーネントはpageのキャッシュ動作を継承します。

app/page.tsx
'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が各インスタンスで同じ値を生成する限りキャッシュエントリが再利用されます。

app/components/bookings.tsx
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
}
app/components/bookings.js
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を追加することで、その関数がキャッシュ可能となり、再利用されるときには同じキャッシュエントリを共有します。

app/actions.ts
export async function getData() {
'use cache'

const data = await fetch('/api/data')
return data
}

再検証

デフォルトでは、Next.jsはuse cacheディレクティブを使用するときに**15分の再検証期間**を設定します。Next.jsは事実上無限の有効期限を設定するため、頻繁に更新される必要がないコンテンツに適しています。

頻繁に変更されることを期待していないコンテンツについて、この再検証期間は便利ですが、cacheLifeおよびcacheTagAPIを使用してキャッシュの動作を構成することもできます:

  • cacheLife: 時間に基づく再検証期間のためのものです。
  • cacheTag: オンデマンドの再検証のためのものです。

これらのAPIはクライアントとサーバーのキャッシングレイヤー全体で統合されており、キャッシングセマンティクスを1か所で設定し、どこにでも適用できます。

詳細については、cacheLifecacheTagのドキュメントをご覧ください。

インターリービング

キャッシュ可能な関数に非シリアライズ可能な引数を渡す必要がある場合は、それをchildrenとして渡すことができます。これにより、children参照を変更してもキャッシュエントリには影響を与えないようになります。

app/page.tsx
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>
)
}

サーバーアクションをキャッシュされたコンポーネントを通してクライアントコンポーネントに渡すこともできますが、それをキャッシュ可能な関数内で呼び出すことはありません。

app/page.tsx
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} />;
}
app/ClientComponent.tsx
'use client'

export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}