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 コンポーネントの数だけコードを変更する必要があります。置換で楽できなくもないですが事故が起きやすくもあります)。