Sponsor
ChatHubChatHub Use GPT-4, Gemini, Claude 3.5 and more chatbots side-by-side
ここをクリック
Menu

use cache

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

使用方法

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

next.config.ts
TypeScript
import type { NextConfig } from 'next'
 
const nextConfig: NextConfig = {
  experimental: {
    useCache: true,
  },
}
 
export default nextConfig

また、dynamicIOフラグが設定されている場合にもuse cacheディレクティブは有効になります。

次に、ファイル、コンポーネント、または関数レベルで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 propとしてJSXを受け取り、<div>{children}</div>を返すことはできますが、実際のchildrenオブジェクトを調査することはできません。
  • キャッシュ可能な関数の戻り値もシリアライズ可能でなければなりません。これにより、キャッシュされたデータが正しく保存および取得できることが保証されます。
  • use cacheディレクティブを使用する関数は、状態の変更、DOMの直接操作、コードを一定間隔で実行するためのタイマーの設定などの副作用を持ってはいけません。
  • 部分的プリレンダリングと一緒に使用する場合、use cacheを持つセグメントは静的HTMLシェルの一部としてプリレンダリングされます。
  • JSONデータのみをサポートするunstable_cacheとは異なり、use cacheはReactがレンダリングできるシリアライズ可能なデータ、コンポーネントのレンダリング出力を含む任意のデータをキャッシュできます。

use cacheによる完全なルートのキャッシング

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

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

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

app/page.tsx
TypeScript
'use 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
TypeScript
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
TypeScript
export async function getData() {
  'use cache'
 
  const data = await fetch('/api/data')
  return data
}

再検証

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

この再検証期間は頻繁に変更されないコンテンツには役立ちますが、キャッシュの動作を設定するためにcacheLifecacheTag APIを使用できます:

  • cacheLife:時間ベースの再検証期間に対応。
  • cacheTag:オンデマンド再検証に対応。

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

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

インターリーブ

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

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

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

app/page.tsx
TypeScript
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
TypeScript
'use client'
 
export default function ClientComponent({
  action,
}: {
  action: () => Promise<void>
}) {
  return <button onClick={action}>Update</button>
}