TL;DR
サーバサイドでのみ実行される React のコンポーネント。コンポーネントの JavaScript はブラウザに送られない。対になるのは Client Component(CC)である。
クライアントアプリケーションや SSR サーバとは別の環境で、バンドル前に事前にレンダーされます。
React Server Components の “server” とはこの別の環境を指しています。サーバコンポーネントは、CI サーバでビルド時に一度だけ実行することも、ウェブサーバを使用してリクエストごとに実行することもできます。
なぜ必要か
コンポーネントを非同期関数で書けるので、コンポーネントから直接外部 API のデータを取得してレンダリングできる。
仕組み
Next.js でのレンダリング
Next.js の App Router では、コンポーネントはデフォルトで Server Component として扱われる。サーバサイドでは、次の 2 段階でレンダリングする。
- RSC から RSC Payload をレンダリングする。RSC Payload は、複数行の JSON をストリームにしたものである
- RSC Payload と CC から、初期表示のための HTML をレンダリングする(SSR)
クライアントには、HTML、RSC Payload、CC の JavaScript バンドルを送る。クライアントサイドでは次の順に処理する。
- HTML を使って、インタラクティブでない画面をすぐに表示する(初回のみ)
- RSC Payload を使って、RSC と CC のツリーを突き合わせ、DOM を更新する
- JavaScript で CC をハイドレーションし、インタラクティブにする
レンダリングは常にサーバサイド、クライアントサイドの順に進む。CC がレンダリングされるときには、SC のレンダリングはすでに終わっている。そのため、CC から SC を import することはできない。
"use client"
import ServerComponent from "./Server-Component"
// できない
export default function ClientComponent() {
return (
<>
<ServerComponent />
</>
)
}一方、SC を props として CC に渡すこと(Composition パターン)はできる。
export default function Page() {
return (
<ClientComponent>
<ServerComponent />
</ClientComponent>
)
}例
async function getPosts() {
const data: { posts: Post[] } = await fetch("https://example.com/posts").then((res) => res.json())
return data.posts
}
export default async function ServerComponent() {
const posts = await getPosts()
return (
<div>
<h1>Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>
<a href={post.url}>{post.title}</a>
</li>
))}
</ul>
</div>
)
}fetch はコンポーネントの中で直接呼ばず、上の getPosts のような関数に切り出す。どんなデータを取得しているかが分かりやすく、Request Memoization の考慮漏れも起きにくい。
リクエストウォーターフォールを避けるには、データを並列に取得する。方法は 2 つある。1 つは Promise.all を使う方法である。
export default async function Page({ params, searchParams }: Props) {
const [photos, category] = await Promise.all([getPhotos(), getCategory(params.categoryName)])
// ...
}もう 1 つは、子の Server Component ごとにデータを取得する方法である。コロケーションを意識したやり方になる。
注意点
従来の React の SSR では、コンポーネントはブラウザとサーバの両方で実行されていた。RSC はよく SSR と混同されるが、別物である。RSC は RSC Payload を、SSR は HTML を出力する。それぞれ独立しているので、同じプロセスやマシンで動かす必要もない。
関連
- Client Component
- Server Component vs Client Component
- use client は境界を宣言する
- RSC Payload
- ハイドレーション
- Server Rendering Strategies
出典
- 実践Next.js ——App Routerで進化するWebアプリ開発
- Server Components - React
- Server Components - Next.js
- Fetching Data: Request Memoization - Next.js
- https://zenn.dev/dai_shi/articles/94affd526f4c8a
- https://zenn.dev/yuu104/articles/react-server-component
- https://zenn.dev/frontendflat/articles/nextjs-rscpayload-composition