【React】props.children を使って決まった構造のコピペや冗長な props リレーを減らす

TL; DR

  • props.children を使うとタグ内部のコンポーネントを呼べます
  • 内部を気にしないフレーム的共通コンポーネントを作ると枠組みを定義する部分をコピペせずに済みます
  • ネストしたコンポーネント構造でもロジック上ではフラットに扱えます
// MyCard.jsx
function MyCard(props) {
  return (
    <div className={'card'}>
      <div className={'card-header'}>{props.header}</div>
      <div className={'card-content'}>
        {props.children}
      </div>
      <div className={'card-footer'}>{props.footer}</div>
    </div>
  );
}
// Hoge.jsx
function Hoge(props){
  return <MyCard>
    <p>define in Hoge</p>
  </MyCard>
}

// Fuga.jsx
function Fuga(props){
  return <MyCard>
    <p>define in Fuga</p>
  </MyCard>
}

本文

 React は web ページ(React は web 以外も得意ですがとりあえず react-dom の web 相当で話を進めます)を構成する要素をいくらかのコンポーネントに分けて記述することを得意とするライブラリです。これを用いることで次の様に web ページの定義を分割できます。

function HogeCard(props){
  return <div className={'card'}>
      <div className={'card-header'}>Hoge</div>
      <div className={'card-content'}>
        <div>
          <p>define in Hoge</p>
          <p>define in Hoge</p>
        </div>
      </div>
      <div className={'card-footer'}></div>
    </div>
}

function FugaCard(props){
  return <div className={'card'}>
      <div className={'card-header'}><h1>Fuga</h1></div>
      <div className={'card-content'}>
        <p>define in Fuga</p>
      </div>
      <div className={'card-footer'}><h1>Fuga</h1></div>
    </div>
}

function App(){
  return <div>
    <HogeCard/>
    <FugaCard/>
  </div>
}

 便利ですが余分な部分が残っています。HogeCard、FugaCard は Card という同じ構造を使っていますが細部が異なります。特に card-content 内部は大きく違っています。これを統一するにはコンポーネント備え付けの porps である children を使うのが便利です。
コンポジション vs 継承 – React
 この children にはタグ内部の ReactNode が格納されます。これは大体 string, JSX.Element あるいは JSX.Element の配列です。これを使うと次の様なコンポーネントを定義できます。

function MyCard(props) {
  return (
    <div className={'card'}>
      <div className={'card-header'}>
        {props.header}
      </div>
      <div className={'card-content'}>
        {props.children}
      </div>
      <div className={'card-footer'}>{props.footer}</div>
    </div>
  );
}

 自分の内部定義、特に card-contet の内部を持たないコンポーネントです。これを用いることで同様の動作をするコードをより簡易に書けます。

// Card としての構造を定義するコンポーネント
function MyCard(props) {
  return (
    <div className={'card'}>
      <div className={'card-header'}>
        {props.header}
      </div>
      <div className={'card-content'}>
        {/* タグ内部をここで展開 */}
        {props.children}
      </div>
      <div className={'card-footer'}>
        {/* 別に children に限らなくても JSX なら展開してもらえます */}
        {props.footer}
      </div>
    </div>
  );
}
// 具体部のみで記述が少ない
function HogeCard(props){
  return <MyCard header={'Hoge'}>
    <div>
      <p>define in Hoge</p>
      <p>define in Hoge</p>
    </div>
  </MyCard>
}

// 具体部のみで記述が少ない
function FugaCard(props){
  return <MyCard header={(<h1>これはJSX</h1>)} footer={(<h1>これもJSX</h1>)}>
    <p>define in Fuga</p>
  </MyCard>
}

function App(props){
  return <div>
    <HogeCard/>
    <FugaCard/>
  </div>
}

 このようにすれば更に FooCard, BarCard などの似た構造のコンポーネントを増やすのが楽になり、Card 構造自体に手を加えるのも簡単です(素のままだと存在する xxxxCard コンポーネントの数だけコードを変更する必要があります。置換で楽できなくもないですが事故が起きやすくもあります)。

>株式会社シーポイントラボ

株式会社シーポイントラボ

TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:〒432-8003
   静岡県浜松市中央区和地山3-1-7
   浜松イノベーションキューブ 315
※ご来社の際はインターホンで「316」をお呼びください

CTR IMG