use cache
use cache
ディレクティブは、コンポーネントや関数をキャッシュするために指定します。ファイルの先頭で使用すると、ファイル内のすべてのエクスポートがキャッシュ可能であることを示し、関数やコンポーネントの先頭でインラインで使用すると、Next.jsに対して返り値をキャッシュし、後続のリクエストで再利用することを通知します。これはNext.jsの実験的な機能であり、Reactのネイティブ機能ではありません(例: use client
やuse server
)。
使用法
next.config.ts
ファイルでdynamicIO
フラグを使用してuse cache
ディレクティブのサポートを有効にします:
- TypeScript
- JavaScript
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
experimental: {
dynamicIO: true,
},
}
export default nextConfig
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
dynamicIO: true,
},
}
module.exports = 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 client
やuse server
)。- キャッシュされた関数に渡されるシリアライズ可能な引数(またはprops)や、親スコープから読み取るシリアライズ可能な値は、JSONのような形式に変換され、自動的にキャッシュキーの一部になります。
- シリアライズ不可能な引数、props、または閉じ込められた値は、キャッシュされた関数内で不透明な参照に変わり、通過させることはできますが、検査や変更はできません。これらのシリアライズ不可能な値はリクエスト時に埋め込まれ、キャッシュキーの一部にはなりません。
- 例えば、キャッシュされた関数はJSXを
children
propとして受け取り、<div>{children}</div>
を返すことができますが、実際のchildren
オブジェクトを内省することはできません。
- 例えば、キャッシュされた関数はJSXを
- キャッシュ可能な関数の返り値もシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得されることが保証されます。
use cache
ディレクティブを使用する関数は、副作用を持ってはいけません。例えば、状態の変更、DOMの直接操作、またはコードを一定間隔で実行するタイマーの設定などです。- Partial Prerenderingと一緒に使用する場合、
use cache
を持つセグメントは静的HTMLシェルの一部としてプリレンダリングされます。 use cache
ディレクティブは、将来的にdynamicIO
フラグとは別に利用可能になります。unstable_cache
がJSONデータのみをサポートするのに対し、use cache
はReactがレンダリングできるシリアライズ可能なデータをキャッシュできます。これには、コンポーネントのレンダリング出力も含まれます。
例
use cache
を使用したルート全体のキャッシュ
ルート全体をプリレンダリングするには、layout
とpage
ファイルの両方の先頭にuse cache
を追加します。これらの各セグメントはアプリケーション内の個別のエントリーポイントとして扱われ、独立してキャッシュされます。
- TypeScript
'use cache'
import { unstable_cacheLife as cacheLife } from 'next/cache'
export default function Layout({ children }: { children: ReactNode }) {
return <div>{children}</div>
}
- JavaScript
'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が各インスタンスで同じ値を生成する限り、キャッシュエントリは再利用されます。
- TypeScript
- JavaScript
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
- JavaScript
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>
)
}
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>
)
}
キャッシュされたコンポーネントを通じてServer ActionsをClient Componentsに渡すこともできますが、キャッシュ可能な関数内でそれらを呼び出すことはありません。
- 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,
}: {
performUpdate: () => Promise<void>
}) {
'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,
}: {
action: () => Promise<void>
}) {
return <button onClick={action}>Update</button>
}
'use client'
export default function ClientComponent({ action }) {
return <button onClick={action}>Update</button>
}