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の別名ではない。