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

use cache

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

使用法

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
}

Good to know

  • use cacheはNext.jsの実験的な機能であり、Reactのネイティブ機能ではありません(例: use clientuse server)。
  • キャッシュされた関数に渡されるシリアライズ可能な引数(またはprops)や、親スコープから読み取るシリアライズ可能な値は、JSONのような形式に変換され、自動的にキャッシュキーの一部になります。
  • シリアライズ不可能な引数、props、または閉じ込められた値は、キャッシュされた関数内で不透明な参照に変わり、通過させることはできますが、検査や変更はできません。これらのシリアライズ不可能な値はリクエスト時に埋め込まれ、キャッシュキーの一部にはなりません。
    • 例えば、キャッシュされた関数はJSXをchildren propとして受け取り、<div>{children}</div>を返すことができますが、実際のchildrenオブジェクトを内省することはできません。
  • キャッシュ可能な関数の返り値もシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得されることが保証されます。
  • use cacheディレクティブを使用する関数は、副作用を持ってはいけません。例えば、状態の変更、DOMの直接操作、またはコードを一定間隔で実行するタイマーの設定などです。
  • Partial Prerenderingと一緒に使用する場合、use cacheを持つセグメントは静的HTMLシェルの一部としてプリレンダリングされます。
  • use cacheディレクティブは、将来的にdynamicIOフラグとは別に利用可能になります。
  • unstable_cacheがJSONデータのみをサポートするのに対し、use cacheはReactがレンダリングできるシリアライズ可能なデータをキャッシュできます。これには、コンポーネントのレンダリング出力も含まれます。

use cacheを使用したルート全体のキャッシュ

ルート全体をプリレンダリングするには、layoutpageファイルの両方の先頭に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>
}
app/page.tsx
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'

export default function Layout({ children }) {
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
}

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はほぼ無限の有効期限を設定し、頻繁な更新が不要なコンテンツに適しています。

この再検証期間は、頻繁に変更が予想されないコンテンツに役立つかもしれませんが、cacheLifecacheTag APIを使用してキャッシュの動作を設定できます:

  • 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 }: { children: ReactNode }) {
'use cache'
const cachedData = await fetch('/api/cached-data')
return (
<div>
<PrerenderedComponent data={cachedData} />
{children}
</div>
)
}

キャッシュされたコンポーネントを通じてServer ActionsをClient Componentsに渡すこともできますが、キャッシュ可能な関数内でそれらを呼び出すことはありません。

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,
}: {
performUpdate: () => Promise<void>
}) {
'use cache'
// ここでperformUpdateを呼び出さない
return <ClientComponent action={performUpdate} />
}
app/ClientComponent.tsx
'use client'

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