密結合を脱却せよ:Compound Componentsが「本当に」現場で愛される理由
フロントエンドの現場で、「propsバケツリレー」に頭を抱えた経験はないだろうか?
親が子へ、子が孫へ…とひたすらデータを渡すだけのコンポーネント。あれはコードの保守性を蝕む癌だ。特に、`Select`や`Tabs`のような、構成要素が多いUIを作る際にこの罠にハマると、propsが肥大化し、型定義も地獄と化す。
そんなとき、中級から一歩先へ進むために習得すべき強力な武器が Compound Components(複合コンポーネント)パターン だ。
1. なぜ「暗黙の連携」が必要なのか
Compound Componentsの真髄は、「親と子の疎結合を保ちつつ、責務を分散させる」ことにある。
例えば、ただの`
// 悪い例:propsが肥大化し、自由度が死んでいる
これに対し、Compound Componentsでは「状態管理は親が、見た目の構成はユーザーが」という分担を行う。ブラウザのDOM構造がそのままReactのコンポーネント構造に投影されるため、エンジニアにとっても直感的だ。
2. 実践:Context APIで「隠れた絆」を作る
ReactのContext APIは、このパターンの接着剤だ。親で状態を管理し、`Provider`で子たちに流し込む。子コンポーネントは、`useContext`を使ってその情報を「盗み見」する。これが、propsを介さずに親子が連携する仕組みだ。
では、現場でそのまま使える「Tabsコンポーネント」を例に実装を見てみよう。
import React, { createContext, useContext, useState, ReactNode } from ‘react’;
// 1. 状態を共有するためのContextを定義
type TabsContextType = {
activeTab: string;
setActiveTab: (id: string) => void;
};
const TabsContext = createContext
// 2. 親コンポーネント:状態管理の黒子
export const Tabs = ({ children }: { children: ReactNode }) => {
const [activeTab, setActiveTab] = useState
return (
);
};
// 3. 子コンポーネント:自身の責務に集中
export const TabTrigger = ({ id, children }: { id: string; children: ReactNode }) => {
const context = useContext(TabsContext);
if (!context) throw new Error(‘TabTriggerはTabs内で使ってください’);
const { activeTab, setActiveTab } = context;
return (
);
};
export const TabPanel = ({ id, children }: { id: string; children: ReactNode }) => {
const context = useContext(TabsContext);
if (!context) throw new Error(‘TabPanelはTabs内で使ってください’);
return context.activeTab === id ?
: null;
};
3. この設計が「現場」で評価される理由
このコードを実際に使う側はこうなる。
見てほしい。親コンポーネントに「何をどう表示するか」という制御を一切書かなくていい。
例えば、「ヘッダー部分に検索バーを挟みたい」と言われても、Reactコンポーネントを間に挿入するだけで完了する。親のpropsを改修する必要がない。これが、大規模開発における「変更への耐性」を生むのだ。
4. シニアからのアドバイス:実装時の落とし穴
最後に、現場で泣きを見ないためのTipsを3つだけ伝えておく。
1. Contextのnullチェックを忘れるな:
`useContext`が`undefined`を返す場合を想定し、必ずエラーを投げること。そうしないと、原因不明のレンダリングエラーに時間を溶かすことになる。
2. 型定義で縛りすぎない:
`Children`の型を厳密に`ReactElement`に限定しすぎると、構成の自由度が下がる。柔軟な構成を許容することで、将来の要件変更に対応できる。
3. ブラウザのレンダリング効率:
Contextの値が変わると、Provider以下のすべてのコンポーネントが再レンダリングされる。あまりに巨大なツリーになるなら、`memo`化やContextの分割を検討する準備を忘れないこと。
Compound Componentsは、単なるデザインパターンではない。「コンポーネントの所有権をUIの消費者に返す」という、Reactの哲学を体現した設計思想だ。これを使いこなせば、君が書くコードは「使い捨て」から「資産」へと変わるはずだ。
次はぜひ、君のプロジェクトにある巨大なコンポーネントを、この手法で分解してみてくれ。コードが驚くほど軽やかになるのを実感できるはずだ。

コメント