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

use cache

use cacheディレクティブを使用すると、ルート、Reactコンポーネント、または関数をキャッシュ可能としてマークできます。ファイルの先頭で使用して、そのファイル内のすべてのエクスポートをキャッシュすることを示したり、関数やコンポーネントの先頭でインラインで使用して、戻り値をキャッシュすることができます。

使用法

use cacheは現在、実験的な機能です。これを有効にするには、next.config.tsファイルにuseCacheオプションを追加します:

next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
experimental: {
useCache: true,
},
}

export default nextConfig

Good to know: use cachedynamicIOオプションでも有効にできます。

次に、ファイル、コンポーネント、または関数レベルで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の動作

キャッシュキー

キャッシュエントリのキーは、その入力のシリアライズされたバージョンを使用して生成されます。これには以下が含まれます:

  • ビルドID(各ビルドで生成される)
  • 関数ID(関数に固有のセキュアな識別子)
  • シリアライズ可能な関数の引数(またはprops)

キャッシュされた関数に渡される引数や、親スコープから読み取る値は自動的にキーの一部になります。つまり、入力が同じである限り、同じキャッシュエントリが再利用されます。

非シリアライズ可能な引数

非シリアライズ可能な引数、props、または閉じ込められた値は、キャッシュされた関数内で参照に変わり、通過させることはできますが、検査や変更はできません。これらの非シリアライズ可能な値はリクエスト時に埋め込まれ、キャッシュキーの一部にはなりません。

たとえば、キャッシュされた関数はJSXをchildren propとして受け取り、<div>{children}</div>を返すことができますが、実際のchildrenオブジェクトを内省することはできません。これにより、キャッシュされたコンポーネント内にキャッシュされていないコンテンツをネストすることができます。

app/ui/cached-component.tsx
function CachedComponent({ children }: { children: ReactNode }) {
'use cache'
return <div>{children}</div>
}

戻り値

キャッシュ可能な関数の戻り値はシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得されることが保証されます。

ビルド時のuse cache

layoutpageの先頭で使用すると、ルートセグメントがプリレンダリングされ、後で再検証できるようになります。

つまり、use cachecookiesheadersのようなリクエスト時APIと一緒に使用することはできません。

実行時のuse cache

サーバーでは、個々のコンポーネントや関数のキャッシュエントリがメモリ内にキャッシュされます。

その後、クライアントでは、サーバーキャッシュから返されたコンテンツがセッションの間、または再検証されるまでブラウザのメモリに保存されます。

再検証中

デフォルトでは、use cacheはサーバー側の再検証期間が15分です。この期間は頻繁な更新を必要としないコンテンツに役立ちますが、cacheLifecacheTag APIを使用して、個々のキャッシュエントリが再検証されるタイミングを設定できます。

  • cacheLife: キャッシュエントリの有効期間を設定します。
  • cacheTag: オンデマンドで再検証するためのタグを作成します。

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

cacheLifecacheTagのAPIドキュメントを参照してください。

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

ルート全体をプリレンダリングするには、layoutpageファイルの両方の先頭にuse cacheを追加します。これらの各セグメントはアプリケーションの個別のエントリーポイントとして扱われ、独立してキャッシュされます。

app/layout.tsx
'use cache'

export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}
app/page.tsx
'use cache'

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

pageファイルにインポートされネストされたコンポーネントは、pageのキャッシュ動作を継承します。

app/page.tsx
'use cache'

async function Users() {
const users = await fetch('/api/users')
// ユーザーをループ処理
}

export default function Page() {
return (
<main>
<Users />
</main>
)
}

Good to know:

  • use cachelayoutまたはpageのみに追加された場合、そのルートセグメントとそこにインポートされたコンポーネントのみがキャッシュされます。
  • ルート内のネストされた子がDynamic APIsを使用している場合、ルートはプリレンダリングから除外されます。

use cacheを使用したコンポーネントの出力のキャッシュ

コンポーネントレベルでuse cacheを使用して、そのコンポーネント内で行われるフェッチや計算をキャッシュできます。シリアライズされた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を追加できるため、コンポーネントやルートのキャッシュに限定されません。ネットワークリクエスト、データベースクエリ、または遅い計算をキャッシュしたい場合があります。

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

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

インターリービング

キャッシュ可能な関数に非シリアライズ可能な引数を渡す必要がある場合は、それらを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>
}