17.1 Server Components
Server Componentsは、クライアントアプリまたはSSRサーバーとは別のサーバー環境で事前にレンダリングされるコンポーネントである。
async function ProductPage({ id }: { id: string }) {
const product = await db.product.findUnique({ where: { id } });
return (
<article>
<h1>{product.name}</h1>
<AddToCartButton productId={product.id} />
</article>
);
}
利用にはRSC対応フレームワーク・バンドラー統合が必要である。
17.2 Server Componentの特徴
- サーバー資源へ直接アクセスできる
- コンポーネントコードをクライアントbundleへ含めずに済む
asyncコンポーネントとしてデータ取得できる- クライアントstate・イベント・Effectは使えない
- Client Componentを子として組み込める
17.3 'use client'
"use client";
import { useState } from "react";
export function AddToCartButton({ productId }: { productId: string }) {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(value => value + 1)}>
カートに追加: {count}
</button>
);
}
'use client'は「ブラウザでしかレンダリングされない」という単純な意味ではない。クライアント側モジュールグラフの境界を宣言する。
Client Componentも初期表示時にサーバーでHTML化されることがある。
17.4 シリアライズ境界
Server ComponentからClient Componentへ渡すpropsは、フレームワークがシリアライズできる値である必要がある。
避ける例:
- 任意のクラスインスタンス
- DOMノード
- 一般の関数
- 循環参照
- 環境固有オブジェクト
Server Functionへの参照など、Reactが特別に扱う値は例外となる。
17.5 Server Functions
'use server'でサーバー実行関数を定義する。
"use server";
export async function updateProfile(formData: FormData) {
const user = await requireUser();
const name = String(formData.get("name") ?? "");
await db.user.update({
where: { id: user.id },
data: { name },
});
}
クライアントから呼べるからといって安全ではない。Server Functionは公開HTTPエンドポイントと同等に考える。
必須事項:
- 認証
- 認可
- 入力検証
- CSRF対策の確認
- レート制限
- 監査ログ
- エラー情報の制御
- トランザクション
17.6 Server Actionとの用語差
- Server Function: サーバーで実行される関数の一般名
- Server Action: Actionとして使われるServer Function
すべてのServer ActionはServer Functionだが、すべてのServer FunctionがActionとは限らない。
17.7 RSCとSSRの違い
| 観点 | RSC | SSR |
|---|---|---|
| 出力 | React用のシリアライズ表現 | HTML |
| 主目的 | サーバー・クライアント間のコンポーネント分割 | 初期HTML生成 |
| クライアントJS削減 | Server Component部分で可能 | hydration対象には必要 |
| 組み合わせ | SSRと併用可能 | RSCなしでも可能 |
| 実行時期 | ビルド時またはリクエスト時 | 通常リクエスト時・ビルド時 |
RSCはSSRの別名ではない。