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

データの取得とストリーミングの方法

このページでは、server componentclient componentでデータを取得する方法、およびデータに依存するコンテンツをストリーミングする方法について説明します。

データの取得

Server Components

Server component でデータを取得するには、次の方法があります:

  1. fetch APIを使用する
  2. ORM またはデータベースを使用する

fetch API を使用する

fetch API を使用してデータを取得するには、コンポーネントを非同期関数に変換し、fetch 呼び出しを待機します。例えば:

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 またはデータベースを使用してデータを取得するには、コンポーネントを非同期関数に変換し、呼び出しを待機します:

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>
)
}

Client Components

client component でデータを取得する方法は2つあります:

  1. React のuse フックを使用する
  2. SWRReact Queryのようなコミュニティライブラリを使用する

use フックを使用する

React のuse フックを使用して、サーバーからクライアントにデータをストリーミングできます。まず、Server component でデータを取得し、Promise を client component に prop として渡します:

app/blog/page.tsx
import Posts from '@/app/ui/posts'
import { Suspense } from 'react'

export default function Page() {
// データ取得関数を待機しない
const posts = getPosts()

return (
<Suspense fallback={<div>Loading...</div>}>
<Posts posts={posts} />
</Suspense>
)
}

次に、client component で 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> boundaryでラップする必要があります。これは、Promise が解決される間、フォールバックが表示されることを意味します。ストリーミングについて詳しく学びましょう。

コミュニティライブラリ

SWRReact Queryのようなコミュニティライブラリを使用して、client component でデータを取得できます。これらのライブラリは、キャッシング、ストリーミング、その他の機能に独自のセマンティクスを持っています。例えば、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 config optionが有効になっていることを前提としています。このフラグは Next.js 15 canary で導入されました。

Server component で async/await を使用すると、Next.js は動的レンダリングを選択します。これは、データがユーザーのリクエストごとにサーバーで取得され、レンダリングされることを意味します。遅いデータリクエストがある場合、ルート全体がレンダリングをブロックされます。

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

ストリーミングを使用したサーバーレンダリングの仕組みストリーミングを使用したサーバーレンダリングの仕組み

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

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

loading.js を使用する

ページと同じフォルダに loading.js ファイルを作成して、データが取得されている間にページ全体をストリーミングできます。例えば、app/blog/page.js をストリーミングするには、app/blog フォルダ内にファイルを追加します。

loading.js ファイルを含むブログフォルダ構造loading.js ファイルを含むブログフォルダ構造
app/blog/loading.tsx
export default function Loading() {
// ここで読み込み中の UI を定義します
return <div>Loading...</div>
}

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

読み込み中の UI読み込み中の UI

舞台裏では、loading.jslayout.js 内にネストされ、page.js ファイルとその下のすべての子を自動的に <Suspense> boundary でラップします。

loading.js の概要loading.js の概要

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

<Suspense> を使用する

<Suspense> を使用すると、ページのどの部分をストリーミングするかをより細かく制御できます。例えば、<Suspense> boundary の外にあるページコンテンツをすぐに表示し、boundary 内のブログ投稿のリストをストリーミングできます。

app/blog/page.tsx
import { Suspense } from 'react'
import BlogList from '@/components/BlogList'
import BlogListSkeleton from '@/components/BlogListSkeleton'

export default function BlogPage() {
return (
<div>
{/* このコンテンツはすぐにクライアントに送信されます */}
<header>
<h1>Welcome to the Blog</h1>
<p>Read the latest posts below.</p>
</header>
<main>
{/* <Suspense> boundary でラップされたコンテンツはストリーミングされます */}
<Suspense fallback={<BlogListSkeleton />}>
<BlogList />
</Suspense>
</main>
</div>
)
}

意味のある読み込み状態の作成

インスタント読み込み状態は、ナビゲーション後にユーザーにすぐに表示されるフォールバック UI です。最良のユーザーエクスペリエンスを提供するために、意味のある読み込み状態を設計し、アプリが応答していることをユーザーに理解させることをお勧めします。例えば、スケルトンやスピナー、またはカバーフォトやタイトルなどの将来の画面の小さくても意味のある部分を使用できます。

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