コンテンツにスキップ
App Routerはじめにデータフェッチング

データのフェッチとストリーミングの方法

このページでは、サーバーコンポーネントクライアントコンポーネントでデータをフェッチする方法を説明します。また、データに依存するコンテンツをストリーミングする方法についても説明します。

データフェッチング

サーバーコンポーネント

サーバーコンポーネントでは、以下の方法でデータをフェッチできます。

  1. fetch API
  2. ORM またはデータベース

fetch API の使用

fetch API でデータをフェッチするには、コンポーネントを非同期関数にし、fetch 呼び出しを await します。例:

app/blog/page.tsx
export default async function Page() {
  const data = await fetch('https://api.vercel.app/blog')
  const posts = await data.json()
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

ORM またはデータベースの使用

サーバーコンポーネントはサーバーでレンダリングされるため、ORM やデータベースクライアントを使用して安全にデータベースクエリを実行できます。コンポーネントを非同期関数にし、呼び出しを await します。

app/blog/page.tsx
import { db, posts } from '@/lib/db'
 
export default async function Page() {
  const allPosts = await db.select().from(posts)
  return (
    <ul>
      {allPosts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

クライアントコンポーネント

クライアントコンポーネントでデータをフェッチするには、次の2つの方法があります。

  1. React のuse フック
  2. SWRReact Query などのコミュニティライブラリ

use フックの使用

React のuse フック を使用して、サーバーからクライアントへデータをストリーミングできます。まず、サーバーコンポーネントでデータをフェッチし、その Promise をクライアントコンポーネントに props として渡します。

app/blog/page.tsx
import Posts from '@/app/ui/posts
import { Suspense } from 'react'
 
export default function Page() {
  // Don't await the data fetching function
  const posts = getPosts()
 
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Posts posts={posts} />
    </Suspense>
  )
}

次に、クライアントコンポーネントで use フックを使用して Promise を読み取ります。

app/ui/posts.tsx
'use client'
import { use } from 'react'
 
export default function Posts({
  posts,
}: {
  posts: Promise<{ id: string; title: string }[]>
}) {
  const allPosts = use(posts)
 
  return (
    <ul>
      {allPosts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

上記の例では、<Posts /> コンポーネントを <Suspense> バウンダリー で囲む必要があります。これにより、Promise が解決される間、フォールバックが表示されます。ストリーミングの詳細については、こちらをご覧ください。

コミュニティライブラリ

クライアントコンポーネントでデータをフェッチするには、SWRReact Query などのコミュニティライブラリを使用できます。これらのライブラリには、キャッシュ、ストリーミング、その他の機能に関する独自のセマンティクスがあります。例:SWR の場合

app/blog/page.tsx
'use client'
import useSWR from 'swr'
 
const fetcher = (url) => fetch(url).then((r) => r.json())
 
export default function BlogPage() {
  const { data, error, isLoading } = useSWR(
    'https://api.vercel.app/blog',
    fetcher
  )
 
  if (isLoading) return <div>Loading...</div>
  if (error) return <div>Error: {error.message}</div>
 
  return (
    <ul>
      {data.map((post: { id: string; title: string }) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

ストリーミング

警告: 以下のコンテンツは、アプリケーションで dynamicIO 設定オプションが有効になっていることを前提としています。このフラグは Next.js 15 Canary で導入されました。

サーバーコンポーネントで async/await を使用すると、Next.js は動的レンダリングを選択します。これは、データがユーザーのすべてのリクエストに対してサーバーでフェッチされ、レンダリングされることを意味します。データリクエストが遅い場合、ルート全体がレンダリングをブロックされます。

初期ロード時間とユーザーエクスペリエンスを向上させるために、ストリーミングを使用してページの HTML を小さなチャンクに分割し、それらのチャンクをサーバーからクライアントへ段階的に送信できます。

How Server Rendering with Streaming Works

アプリケーションでストリーミングを実装するには、2つの方法があります。

  1. loading.js ファイルを使用
  2. React の<Suspense> コンポーネントを使用

loading.js の使用

loading.js ファイルをページの同じフォルダーに作成することで、データフェッチ中にページ全体をストリーミングできます。例えば、app/blog/page.js をストリーミングするには、app/blog フォルダー内にファイルを追加します。

Blog folder structure with loading.js file
app/blog/loading.tsx
export default function Loading() {
  // Define the Loading UI here
  return <div>Loading...</div>
}

ナビゲーション時、ユーザーはレイアウトとローディング状態をすぐに確認でき、その間にページがレンダリングされます。レンダリングが完了すると、新しいコンテンツが自動的に入れ替わります。

Loading UI

舞台裏では、loading.jslayout.js の中にネストされ、page.js ファイルとそれ以降の子要素を自動的に <Suspense> バウンダリーで囲みます。

loading.js overview

このアプローチはルートセグメント(レイアウトとページ)に効果的ですが、よりきめ細かなストリーミングには <Suspense> を使用できます。

<Suspense> の使用

<Suspense> を使用すると、ページのどの部分をストリーミングするかをより細かく制御できます。たとえば、<Suspense> バウンダリーの外側にあるページコンテンツはすぐに表示し、バウンダリー内のブログ記事のリストをストリーミングすることができます。

app/blog/page.tsx
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'
 
export default function BlogPage() {
  return (
    <div>
      {/* This content will be sent to the client immediately */}
      <header>
        <h1>Welcome to the Blog</h1>
        <p>Read the latest posts below.</p>
      </header>
      <main>
        {/* Any content wrapped in a <Suspense> boundary will be streamed */}
        <Suspense fallback={<BlogListSkeleton />}>
          <BlogList />
        </Suspense>
      </main>
    </div>
  )
}

意味のあるローディング状態の作成

即時ローディング状態とは、ナビゲーション後にユーザーにすぐに表示されるフォールバック UI です。最高のユーザーエクスペリエンスのために、アプリが応答していることをユーザーが理解できるような、意味のあるローディング状態を設計することをお勧めします。たとえば、スケルトンやスピナー、またはカバー写真、タイトルなど、今後の画面の小さくても意味のある部分を使用できます。

開発中は、React Devtools を使用して、コンポーネントのローディング状態をプレビューおよび検査できます。

API リファレンス

このページで言及されている機能の詳細については、API リファレンスをお読みください。