【実務・中級編】 コンポーネントの合成(Composition) – React実践ガイド

「継承」を捨て、「合成」で勝つ。Reactコンポーネント設計の極意

やあ。現場でコードを書いていて、こんな経験はないかな?

「汎用的な基底コンポーネントを作ったけど、例外的なパターンが増えるたびに `if-else` や膨大なPropsが増殖して、最終的に誰も触りたくないモンスターコードが完成した」

もし心当たりがあるなら、それはReactの設計思想である「合成(Composition)」の力をまだフルに引き出せていない証拠だ。オブジェクト指向の「継承」に慣れた頭を一度リセットしよう。Reactにおいて、再利用性は「クラスの継承」ではなく「コンポーネントの合成」から生まれるんだ。

—

なぜ「合成」が最強の選択肢なのか

Reactの仮想DOMは、コンポーネントツリーを「関数がネストされたツリー構造」として捉えている。ブラウザ上のDOMは単なる木構造だが、Reactはその一つひとつの枝や葉を、疎結合なパーツとして組み立てることを推奨している。

継承(Inheritance)を使ってUIを構築しようとすると、親の振る舞いが子に強制される「密結合」に陥る。しかし、合成は違う。「箱の中に何を入れるか」を呼び出し側で決める。この「制御の反転」こそが、ReactをReactたらしめている真髄だ。

—

現場で即戦力になる「スロットパターン」

実務で最も直感的に使えるのが、`children` を活用したスロットパターンだ。複雑なUIを、外枠(レイアウト)と中身(コンテンツ)に分離するだけで、コードの見通しは劇的に良くなる。

悪い例:Propsで無理やり制御するコンポーネント

// どこが悪いか:タイトル、ボタン、アイコン…とPropsが増え続け、管理不能になる
const Card = ({ title, showButton, buttonText, onClick }) => (

{title}

{showButton && }

);

良い例:合成による柔軟な設計

// Cardは「枠」に徹する。中身は呼び出し側で自由に注入する
const Card = ({ children }) => {
return

{children}

;
};

// これにより、Cardの内部構造を知らなくても、好きなUIを組み立てられる
const UserProfile = () => (

ユーザー名



);

—

さらに深く:名前付きスロットで責務を分離する

`children` だけでは足りない、より複雑なUIの場合は、コンポーネントをPropsとして渡す「名前付きスロット」という手法をとる。これができると、チーム開発の効率は一気に上がる。

// 複数のエリアを持つレイアウトコンポーネント
const PageLayout = ({ header, sidebar, main }) => {
return (

{header}

{main}

);
};

// 呼び出し側(これが驚くほど綺麗になる)
const Dashboard = () => (
}
sidebar={}
main={}
/>
);

なぜこれが「実務レベル」で重要なのか?

1. テストの簡素化: `PageLayout` 自体のテストは「各スロットが正しく配置されるか」を確認するだけでいい。中身のロジックと分離されているため、バグの切り分けが非常に容易になる。
2. Prop Drillingの回避: 深い階層にPropsをリレーさせる必要がない。必要なコンポーネントをその場で注入するからだ。
3. カプセル化: `PageLayout` は中のコンポーネントが何をしているかを知らなくていい。ただ「そこにあるものを表示する」という責務に専念できる。

—

現場のシニアからのアドバイス

「何でもかんでもコンポーネントに分割すればいい」というのは罠だ。
「変更の理由が同じものをまとめ、変更の理由が異なるものを分ける」。これがコンポーネント設計の唯一の黄金律だ。

合成を意識し始めると、自然とコンポーネントは「純粋」になっていく。ブラウザが仮想DOMをレンダリングする際も、分割されたコンポーネントの方が差分検出(Reconciliation)の対象を絞り込みやすく、パフォーマンス上のメリットも享受できる。

まずは、今書いている巨大なコンポーネントのPropsを見てほしい。「これって、`children` で渡せるんじゃないか?」と自問自答する。その一歩から、君のコードは劇的に美しく、そして堅牢に変わっていくはずだ。

迷ったら、シンプルに組み立てろ。Reactは、君が想像する以上に君の自由を尊重してくれるフレームワークなんだから。

コメント

タイトルとURLをコピーしました