LoginSignup
1
0

【React】Compound Components

Posted at

はじめに

業務の中でデザインパターンの一つである Compound Components というものを知ったので、どんなふうに使うのか?を記事にまとめてみました。

Compound Components とは

Compound Components は、全体を親コンポーネントで囲み、子コンポーネントをユーザーに指定させるパターンです。
データを Props 経由で渡し、一つのコンポーネントの中で全てを処理するのではなく、子コンポーネントとして渡すようにします。

Compound Components の利用例

Header / Body / Footer を共通レイアウト化して使えるようにしたいが、ページによって少しカスタマイズしたい、といったケースで考えてみます。

Compound Components を使わない実装例

「Footer は全ページ共通だが、Header は条件により出し分けたい」とします。

type Props = {
  globalMenuList?: GlobalMenu[]
  pageTitle: string
  children: ReactNode
}

export const CommonLayout: React.FC<Props> = ({ globalMenuList, pageTitle, children }) => (
  <div>
    {globalMenuList ? (
      <Header  pageTitle={pageTitle}>
        <GlobalMenu globalMenuList={globalMenuList} />
      </Header>
    ) : (
      <Header pageTitle={pageTitle} />
    )}
    {children}
    <Footer />
  </div>
)

↓使用イメージ

<CommonLayout globalMenuList={globalMenuList}>
  <div>
    body の中身
  </div>
</CommonLayout>

JSX のなかで条件によって表示を出し分けたいときは条件付きレンダーを使うことが多いと思います。

上記のコード例は非常にシンプルなのでそこまで複雑には見えないですが、実務だとコード量がさらに大きく、条件つきレンダーを使用する箇所も増えてくるため、見づらく感じることがあります。

また、Header まで全て CommonLayout のなかで表現しようとすると、レイアウトの共通化をしたい目的で作っているのに、 CommonLayout が知っていることが増えてしまっています。

Compound Components を用いた実装例

CommonLayout から Header 部分を CommonLayoutHeader に切り出してみます。

type CommonLayoutHeaderProps = {
  children?: GlobalMenu[]
  pageTitle: string
}

type CommonLayoutProps = {
  children: ReactNode
}

export const CommonLayoutHeader: React.FC<CommonLayoutHeaderProps> = ({ children, pageTitle }) => (
  <Header pageTitle={pageTitle}>
    {children}
  </Header>
)

export const CommonLayout: React.FC<CommonLayoutProps> = ({ children }) => (
  <div>
    {children}
    <Footer />
  </div>
)

↓使用イメージ

// グローバルメニューを出したいページでは children として GlobalMenu を渡す
<CommonLayout>
  <CommonLayoutHeader  pageTitle={pageTitle}>
    <GlobalMenu globalMenuList={globalMenuList} />
  </CommonLayoutHeader>
  <div>
    body の中身
  </div>
</CommonLayout>

// グローバルメニューを出さないページでは children を渡さないようにする
<CommonLayout>
  <CommonLayoutHeader  pageTitle={pageTitle} />
  <div>
    body の中身
  </div>
</CommonLayout>

使う側から GlobalMenu を渡すことができるようになり、 適切に CommonLayout との責務の分割ができています!

さいごに

なんでも props として渡すと渡すものがどんどん膨れ上がったり、props が多いと「これを渡すとどうなるのか?」がどんどん複雑になっていってしまいます。
こんなときに Compound Components を取り入れ、データを子コンポーネントへ渡すようにすることで直感的にも使いやすいし、責務の分割も適切にできるようになる、と感じました。

適材適所で使い分けができるようになっていきたいです。

参考

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0