おお、同志よ!Reactの深淵なる問い、「包含(Composition)」か「設定(Configuration)」か という聖戦に足を踏み入れたな!😇
Reactにおける children は、我らが神LISPにおける S式(S-Expression)のネスト構造 そのものである。
(parent (child)) という構造を愛する我らにとって、children は極めて自然な表現だ。
しかし、エンジニアとして ロジックの結合度(Coupling) と 認知負荷(Cognitive Load) を最適化せねばならん。
結論から言えば、「そのコンポーネントが中身を知る必要があるか?」 で決まる。
神の審判(使い分けの基準)を授けよう。
🏛️ 1. children を使うべき時: 「器(Container)」としての責務
コンポーネントが「ただの枠」であり、中身に何が来るか関知しない場合、children が至高である。
これを 制御の反転(Inversion of Control) と呼ぶ。
✅ 適切なケース
- Card, Modal, Layout, Button
- 中身がテキストなのか、画像なのか、別のコンポーネントなのか、親が決めるべき場合。
// LISP的思考: (Card (Text "神よ"))
// コンポーネントは「枠」を提供するだけ
const Card = ({ children }: { children: React.ReactNode }) => {
return <div className="p-4 border shadow">{children}</div>;
};
// 使う側(自由度が高い)
<Card>
<h1>神への祈り</h1>
<p>Lisp is God.</p>
</Card>
- メリット: 結合度が低い。再利用性が高い。
- デメリット: 子要素の配置場所が「一箇所」に限られる(基本的には)。
🔧 2. props を使うべき時: 「部品(Part)」としての責務
コンポーネントがデータを特定の場所に埋め込んだり、中身の型を厳密に縛りたい場合、props が正義である。
✅ 適切なケース
- UserAvatar, ProductPrice, specific UI widget
- 「タイトルはここ」「画像はここ」とレイアウトが厳密に決まっている場合。
// コンポーネントが構造を支配している
type UserCardProps = {
name: string;
role: string;
};
const UserCard = ({ name, role }: UserCardProps) => {
return (
<div className="border">
{/* 配置場所はUserCardが決める */}
<h2 className="font-bold">{name}</h2>
<span className="text-gray-500">{role}</span>
</div>
);
};
- メリット: 型安全性(Type Safety)が高い。デザイン崩れを防げる。
- デメリット: 柔軟性がない。中身を変えるにはコンポーネント自体の修正が必要。
⚔️ 3. 第三の道: 「スロット(Slots)」パターン
ここからがエンジニアの腕の見せ所だ。
「枠ではあるが、穴が複数ある」 場合、children ではなく ReactNode を props で渡す のだ。
✅ 適切なケース
- Layout (Header / Sidebar / Main), Dialog (Title / Body / Footer)
type LayoutProps = {
header: React.ReactNode; // 第一のスロット
sidebar: React.ReactNode; // 第二のスロット
children: React.ReactNode; // メインコンテンツ
};
const AppLayout = ({ header, sidebar, children }: LayoutProps) => {
return (
<div className="grid">
<header>{header}</header>
<aside>{sidebar}</aside>
<main>{children}</main>
</div>
);
};
// 使う側:名前付き引数のように渡せる(まさにLispのキーワード引数!)
<AppLayout
header={<Navbar />}
sidebar={<Menu />}
>
<p>Main Content</p>
</AppLayout>
⚖️ 最終審判アルゴリズム
迷った時は、以下のロジックで で決定せよ。
- 「そのコンポーネントは、中身の配置を1箇所にまとめたいか?」
- Yes 👉
children(汎用的なラッパー)
- 「そのコンポーネントは、中身のデータ構造を知っている必要があるか?」
- Yes 👉
props(string, int, object) (専用コンポーネント)
- 「そのコンポーネントには、中身を入れる穴が2つ以上あるか?」
- Yes 👉
props(ReactNode) (スロットパターン)
同志よ、Reactにおける「合成」はLispの精神そのものだ。
「抽象度」 のコントロールなのだ。