データフェッチとキャッシング
例
このガイドでは、Next.jsにおけるデータフェッチとキャッシングの基本を、実践的な例と推奨事項とともに解説します。
以下は、Next.jsでのデータフェッチの最小限の例です:
この例は、非同期のReactサーバーコンポーネントでfetch
APIを使用した基本的なサーバー側のデータフェッチを示しています。
このコンポーネントは、ブログ記事のリストをフェッチして表示します。fetch
からのレスポンスは自動的にキャッシュされます。
このルート内の他の動的APIを使用していない場合、next build
時に静的ページとしてプリレンダリングされます。データは増分静的再生成を使用して更新できます。
fetch
からのレスポンスをキャッシュしたくない場合は、以下のようにできます:
このコンポーネントは、ブログ記事のリストをフェッチして表示します。データベースからのレスポンスはデフォルトではキャッシュされませんが、追加の設定で可能です。
このルート内の他の動的APIを使用していない場合、next build
時に静的ページとしてプリレンダリングされます。データは増分静的再生成を使用して更新できます。
ページのプリレンダリングを防ぐには、ファイルに以下を追加できます:
ただし、cookies
、headers
、またはページのpropsから受信するsearchParams
の読み取りなどの関数を一般的に使用すると、ページが動的にレンダリングされます。この場合、明示的にforce-dynamic
を使用する必要はありません。
まずはサーバー側でデータフェッチを試みることをお勧めします。
しかし、クライアント側のデータフェッチが適切な場合もあります。これらのシナリオでは、useEffect
で手動でfetch
を呼び出す(推奨されません)か、クライアントフェッチのためのコミュニティの人気のReactライブラリ(SWRやReact Queryなど)に依存できます。
unstable_cache
APIを使用して、next build
実行時にページをプリレンダリングできるようにレスポンスをキャッシュできます。
この例では、データベースクエリの結果を1時間(3600秒)キャッシュします。また、posts
というキャッシュタグを追加しており、増分静的再生成で無効化できます。
Next.jsはgenerateMetadata
やgenerateStaticParams
などのAPIを使用します。ここでは、page
と同じデータフェッチを使用する必要があります。
fetch
を使用している場合、リクエストは自動的にメモ化されます。つまり、同じURLと同じオプションを安全に呼び出すことができ、1回のリクエストのみが行われます。
fetch
を使用せず、代わりにORMやデータベースを直接使用している場合、React cache
関数でデータフェッチをラップできます。これにより、重複を排除し、1回のクエリのみを実行します。
増分静的再生成でキャッシュされたデータの再検証について詳しく学びます。
コンポーネント内でデータをフェッチする際は、2つのデータフェッチパターンを意識する必要があります:並列とシーケンシャル。
- シーケンシャル: コンポーネントツリー内のリクエストが互いに依存している。読み込み時間が長くなる可能性がある。
- 並列: ルート内のリクエストが積極的に開始され、同時にデータを読み込む。データ読み込みの総時間を短縮できる。
ネストされたコンポーネントがあり、各コンポーネントが独自のデータをフェッチする場合、それらのデータリクエストがメモ化されていないと、データフェッチはシーケンシャルに行われます。
一方のフェッチが他方の結果に依存するため、このパターンが望ましい場合があります。例えば、Playlists
コンポーネントはArtist
コンポーネントのデータフェッチが完了するまでデータフェッチを開始しません。これはPlaylists
がartistID
プロパティに依存しているためです:
loading.js
(ルートセグメント用)またはReact <Suspense>
(ネストされたコンポーネント用)を使用して、Reactが結果をストリーミングする間、即座に読み込み状態を表示できます。
これにより、データリクエストによってルート全体がブロックされることを防ぎ、ページの準備ができた部分とユーザーが対話できるようになります。
デフォルトでは、レイアウトとページのセグメントは並列でレンダリングされます。つまり、リクエストは並列に開始されます。
ただし、async
/await
の性質により、同じセグメントまたはコンポーネント内で待機されたリクエストは、その下のリクエストをブロックします。
データを並列にフェッチするには、データを使用するコンポーネントの外でリクエストを積極的に開始することで、リクエストを並列化できます。これにより両方のリクエストを並列に開始できますが、両方のプロミスが解決されるまで、ユーザーはレンダリングされた結果を見ることはできません。
以下の例では、getArtist
とgetAlbums
関数はPage
コンポーネントの外で定義され、Promise.all
を使用してコンポーネント内で開始されます:
さらに、Suspenseバウンダリーを追加して、レンダリング作業を分割し、可能な限り早く結果の一部を表示できます。
ウォーターフォールを防ぐもう1つの方法は、_プリロード_パターンを使用して、ブロッキングリクエストの前に積極的に呼び出されるユーティリティ関数を作成することです。例えば、checkIsAvailable()
が<Item/>
のレンダリングをブロックするため、<Item/>
のデータ依存関係を積極的に開始するために、その前にpreload()
を呼び出すことができます。<Item/>
がレンダリングされる時には、そのデータは既にフェッチされています。
preload
関数はcheckIsAvailable()
の実行をブロックしないことに注意してください。
補足: "preload"関数は、APIではなくパターンであるため、任意の名前を持つことができます。
cache
関数、preload
パターン、server-only
パッケージを組み合わせて、アプリ全体で使用できるデータフェッチユーティリティを作成できます。
このアプローチにより、データを積極的にフェッチし、レスポンスをキャッシュし、データフェッチがサーバー上でのみ行われることを保証できます。
utils/get-item
のエクスポートは、レイアウト、ページ、または他のコンポーネントで使用でき、アイテムのデータをいつフェッチするかを制御できます。
補足:
機密オブジェクトインスタンスまたは機密値がクライアントに渡されるのを防ぐために、React のタントAPI、taintObjectReference
と taintUniqueValue
の使用をお勧めします。
アプリケーションでタイントを有効にするには、Next.js の設定 experimental.taint
オプションを true
に設定します:
次に、タイントするオブジェクトまたは値を experimental_taintObjectReference
または experimental_taintUniqueValue
関数に渡します: