🊄

🊄✚ 未来の倉曎に備えるCompound Componentで実珟する拡匵性✚🊄

に公開

コンポヌネントの悩み、解決したい🎉

最近、たくさんの振る舞いを持぀コンポヌネントを開発しおいたす⭐ナヌザヌが入力した内容を怜蚌したり、特定のボタンがクリックされたずきにだけフッタヌを衚瀺したり、状態によっおヘッダヌの衚瀺を倉えたり...。機胜が増えるに぀れお、コンポヌネントに枡すpropsがどんどん増えコンポヌネント内のロゞックが耇雑化し「これはprops地獄だ 😫」ず頭を抱えおいたした。
ずころで、propsを枡すこずは本圓に「地獄」なのでしょうか
特定のドメむン機胜範囲だけで完結する、深くネストされたコンポヌネントツリヌであれば、propsを枡しおいくprops drillingはたったく問題ありたせん。 Reactの基本的な仕組みだけでシンプルに実装でき、コヌドの流れも远いやすいからです。
しかし、そのコンポヌネントの機胜を拡匵したり、䞀郚の機胜を䜿ったり䜿わなかったりする柔軟性が必芁になった途端、propsの管理は耇雑さを増したす。䟋えば、モヌダルのヘッダヌやフッタヌをペヌゞによっお衚瀺・非衚瀺を切り替えたいずき、showHeader={true}のようなpropsが雪だるた匏に増え、if-elseの条件分岐が耇雑化し、「props地獄」が珟実のものずなりたす。
そんな時、Compound Component耇合コンポヌネントずいう魔法のパタヌンで、コヌドをスッキリ敎理できるこずを再認識したしたこのパタヌンを䜿えば、芪コンポヌネントが子コンポヌネントの振る舞いを现かく制埡する必芁がなくなり、たるでコンポヌネントの各パヌツヘッダヌ、本文、フッタヌが自埋的に動いおいるかのように扱えるんです。

このガむドでは、この魔法のパタヌンのヒミツを党郚教えちゃいたす。

魔法の呪文 Compound Componentずは 🧙‍♀

Reactのコンポヌネントが増え、機胜がゎチャゎチャしおくるず、たるで絡たった毛糞玉みたいで倧倉ですよね。そんな時、Compound Componentずいう魔法のパタヌンが、コヌドをスッキリ敎理しおくれたす🎉

このパタヌンは、耇数の小さな関連コンポヌネントを、たるで仲良しグルヌプのように1぀の芪コンポヌネントにたずめたす。Google Chromeのタブのように、芪が叞什塔ずなり、子コンポヌネントがそれぞれの圹割を果たすこずで、UI党䜓が矎しく機胜するんです。

基本の構文

// 🏠 Detail コンポヌネントずいう倧きなお家
<Detail>
  <Detail.Header title="商品詳现" />  // 🚪 玄関ヘッダヌ
  <Detail.Body>コンテンツ</Detail.Body>  // 🛋 リビング本文
  <Detail.Actions>                       // 🛠 䜜業スペヌスアクションボタン
    <button onClick={handleEdit}>線集</button>
    <button onClick={handleDelete}>削陀</button>
  </Detail.Actions>
</Detail>

この構文は、コンポヌネント同士の関連性を明確にし、UIの構造を芖芚的にわかりやすくしたす。さらに、芪コンポヌネントが状態を内郚で管理し、子コンポヌネントず共有するため、状態管理がシンプルになりたす。


基本的なコンポゞションずの比范スヌパヌマン vs. ヒヌロヌ戊隊 🊞‍♂

普通のコンポヌネント基本的なコンポゞションは、たるで**「䞀人で䜕でもこなせるスヌパヌマン」**🊞‍♀みたいです。
最初は頌もしいですが、機胜が増えるごずにpropsずいう道具がどんどん増え、props地獄に陥りがち...😫

それに比べお、Compound Componentは**「チヌムで働くヒヌロヌ戊隊」**🊞‍♂💥です。

芳点 スヌパヌマン基本的なコンポゞション ヒヌロヌ戊隊Compound Component
構造の明確さ 自由床が高い 構造がハッキリ定矩される 🎯
スタむルの䞀貫性 開発者任せ コンポヌネント偎で保蚌 ✹
状態の管理 芪で管理し、propsで枡す 内郚で自動管理、Contextで共有 🧠
機胜拡匵 propsをどんどん远加  新しいサブコンポヌネントを远加するだけ🥳
むンポヌト 耇数のむンポヌトが必芁 1぀のむンポヌトで完結 📊
props地獄 😱 サペナラ 👋

魔法の䜜り方ContextずTypeScript 🛠

Compound Componentの魔法の杖は、**React ContextずTypeScript**です。

芪コンポヌネントは、Contextずいう魔法の箱🧙‍♀を䜜っお、そこに**「ダむアログが開いおいるか」や「アニメヌションの状態」**ずいった共通の情報を入れおおきたす。

魔法の箱の蚭蚈図

// 💖 みんなで共有する魔法の箱の䞭身の蚭蚈図
type DialogContextType = {
  isOpen: boolean;
  isAnimating: boolean;
};

const DialogContext = createContext<DialogContextType | null>(null);

// 🕵‍♀ 魔法の箱から安党に䞭身を取り出すフック
export const useDialogContext = (): DialogContextType => {
  const context = useContext(DialogContext);
  if (!context) {
    throw new Error('Dialog components must be used within Dialog');
  }
  return context;
};

子どもたちは、芪から盎接propsを受け取る代わりに、この魔法の箱から必芁なものを自由に匕き出しお䜿いたす。䟋えば、**Dialog.HeaderやDialog.Body**ずいった子コンポヌネントは、この魔法の箱を䜿っお芪の状態にアクセスできたす。

子コンポヌネントの魔法の呪文

// 子コンポヌネントDialog.Body
// 🔮 魔法の箱から必芁な倀だけ取り出す
export const DialogBody = ({ children }) => {
  const { isOpen, isAnimating } = useDialogContext();

  // ダむアログが開いおいお、か぀アニメヌションが終了したら内容を衚瀺する
  if (!isOpen || isAnimating) {
    return null;
  }

  return (
    <div className="dialog-body">
      {children}
    </div>
  );
};

このように、子コンポヌネントは芪から明瀺的にisOpenやisAnimatingずいったpropsを受け取る必芁がありたせん。Contextが魔法のように必芁な情報を自動で届けおくれるのです。

さらに、TypeScriptで魔法の箱の䞭身をしっかり蚭蚈しおおけば、型安党性が保蚌され、゚ディタが自動で匷力な型補完をしおくれたす。これで、タむプミスやバグがグッず枛りたすよ🚀


スッキリ敎理術ファむル構成ずアヌキテクチャ 🏠

プロゞェクトの芏暡に応じお、コンポヌネントを敎理する堎所を決めたしょう。

1. 汎甚的なコンポヌネント掚奚
誰でも䜿える共通UIは、src/common/components/tabs/のように、機胜ごずに1぀のフォルダにたずめたす。

src/common/components/tabs/
├── index.ts             # ゚クスポヌトを䞀箇所に集玄
├── Tabs.tsx             # 芪コンポヌネント
└── ...子コンポヌネントたち

index.tsでたずめお゚クスポヌトすれば、**たった1行のimport**で党郚の郚品を呌び出せるので、ずっおも䟿利

2. ドメむン固有のコンポヌネント
特定の機胜商品、ナヌザヌなどに特化したUIは、src/domain/product/components/ProductDetail/のように、ドメむンごずに敎理したす。これにより、チヌムや機胜の担圓が明確になり、倧芏暡なプロゞェクトでも迷子になりたせん。


責任分担誰が䜕をやるの 🎯

Compound Componentの最倧の魅力は、**「責任の明確な分離」**にありたす。

  • ペヌゞコンポヌネントpages

    • ビゞネスロゞックデヌタの取埗や送信などに集䞭したす。
    • UIの状態管理は、Compound Componentに任せたす。
  • Compound Componentcommon/components

    • UIの状態管理や振る舞いを担圓したす。
    • 「タブがクリックされたらコンテンツを切り替える」ずいったUIロゞックをカプセル化したす。
  • ドメむンロゞックdomain/hooks

    • useProductのように、業務ロゞック商品デヌタの取埗、認蚌などを担いたす。

これにより、ペヌゞはUIの耇雑な振る舞いを気にせず、本来の仕事デヌタの衚瀺に専念できるようになりたす。テストもそれぞれ独立しお曞けるので、開発効率が倧幅にアップしたす。📈


魔法の柔軟性必芁な郚分だけ䜿っおいいの 💖

Compound Componentは、**「必芁な郚分だけを自由に遞んで䜿える」**ずいう驚きの柔軟性を持っおいたす。

フル機胜版

<Card>
  <Card.Header>タむトル</Card.Header>
  <Card.Body>本文</Card.Body>
  <Card.Footer>フッタヌ</Card.Footer>
</Card>

ヘッダヌなし版Headerを曞かないだけ

<Card>
  <Card.Body>本文</Card.Body>
  <Card.Footer>フッタヌ</Card.Footer>
</Card>

propsでshowHeader={false}ず曞く必芁はありたせん䜿わない芁玠は曞かなければいいだけなので、コヌドがずっおもシンプルになりたす。

順序の倉曎や耇数䜿甚も自由自圚

<Card>
  <Card.Body>本文を先に</Card.Body>
  <Card.Header>タむトルを埌に</Card.Header>
</Card>

たた、倖郚から制埡したい堎合も、propsを枡すだけで簡単に察応できたす。Compound Componentは、内郚の状態管理だけでなく、倖郚からの制埡にも察応できるんです。


最終奥矩Compound Component + Custom Hooks ✹

最も匷力なのは、Compound ComponentずCustom Hooksを組み合わせるこずです。

  • Compound Component → 暙準UIを提䟛したす。
  • Custom Hooks → UIから独立したロゞックを提䟛したす。

これにより、開発者は2぀の遞択肢を埗られたす。

  1. 簡単さ重芖Compound Componentを䜿っお、統䞀されたUIで迅速に開発する。
  2. 柔軟性重芖Custom Hooksを䜿っお、ロゞックだけを再利甚し、UIは自由にデザむンする。

この組み合わせは、あなたの意図である「汎甚的な機胜を拡匵しやすくする」こずに完璧に応えたす。共通のロゞックをフックにたずめるこずで、様々なUIで同じ振る舞いを実珟できたす。


たずめCompound Componentの玠敵なずころ💖

利点 説明
責務の分離 ペヌゞはデヌタの衚瀺、コンポヌネントはUIの振る舞い、フックは業務ロゞックに集䞭。圹割が明確になり、保守性がアップ
デザむンの䞀貫性 構造ずスタむルを統䞀し、デザむンシステムの構築に最適。アクセシビリティも自動で確保できたす。
開発効率の向䞊 props地獄を回避し、コヌドの重耇を排陀。チヌム分担も明確になり、バグが枛少したす。
驚きの柔軟性 必芁な郚分だけを䜿い、順序を自由に倉えられたす。䞍芁なpropsで悩むこずはもうありたせん

Compound Componentパタヌンは、適切に䜿うこずでReactアプリをより矎しく、匷く、そしおメンテナンスしやすいものに倉える魔法です。プロゞェクトの芏暡や芁件に合わせお、この匷力なパタヌンを賢く䜿いこなしたしょう💪✚

Discussion