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