IT-Q

では、Client Component の中で Server Component を使いたい場合はどうしますか。import すればいいですか。

import すると巻き込まれるので、children や props として渡します。境界が引かれるのはモジュール依存グラフであってレンダーツリーではない、という違いが効いてきます。

面接官が見ているのはモジュールツリーとレンダーツリーの区別。ここが本命。

これは「'use client' を付けたファイルが import しているユーティリティ関数は、クライアントに送られますか。」への追撃質問です。

よくある答えと、面談でどう見られるか

  • import せず、children や props として渡す。渡した側はサーバーで描画済みになる
    正解です。境界が引かれるのはモジュール依存グラフであって、レンダーツリーではありません。
  • Server Component 側にも 'use client' を付けて、両方をクライアントに寄せる
    それは問題を解かずに諦めています。サーバーでしかできないこと(DB接続、秘密情報の利用)ができなくなりますし、バンドルも増えます。
  • import で問題ない。Next.js が自動でサーバー側に残してくれる
    自動では残りません。import した時点で Client Component のモジュールグラフに入ります。これが「知らないうちにバンドルが膨らむ」典型的な経路です。
  • Server Component の結果を props で渡す。ただし children はレンダーツリーの話なので関係ない
    前半は合っています。ただし children も props の一種で、まさにこの用途で使われる形です。公式の例でも Modal の children に Server Component を渡しています。「モジュールグラフに入らない」という点が本質で、children か他の props かは問いません。

面接官は何を見ているか

  • import ではなく children / props で渡す、と答えた
  • モジュール依存グラフとレンダーツリーは別物だと説明できた
  • 渡した Server Component はサーバーで描画済みになると言えた

模範解答

import すると巻き込まれるので、children(または props)として渡します。

鍵は、'use client' が境界を引くのはモジュール依存グラフであって、レンダーツリーではないという点です。Client Component が import したものはクライアント扱いになりますが、children として渡されたものは Client Component のモジュールグラフに入りません。公式も「children や他の props として渡された Server Component には適用されない。それらはサーバーでレンダリングされ、レンダリング結果として Client Component に渡される」と明記しています。

具体的には、Modal のような入れ物を Client Component にして、中身の Server Component は親(サーバー側)で組み立てて children で流し込みます。

  // app/page.tsx はサーバー
  <Modal>          {/* 'use client' */}
    <Cart />       {/* Server Component のまま */}
  </Modal>

RSC ペイロードには、サーバーで描画した結果と、Client Component を差し込む位置のプレースホルダが入っています。だからこの合成が成立します。

面談では「Provider をどこに置くか」で同じ話が出ます。Context は Server Components では使えないので、children を受け取る Client Component を作り、layout の中でそれに children を渡す形にします。Provider を Client Component にしても、その下が全部クライアントになるわけではありません。