では、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 にしても、その下が全部クライアントになるわけではありません。