TL; DR
- props.children を使うとタグ内部のコンポーネントを呼べます
- 内部を気にしないフレーム的共通コンポーネントを作ると枠組みを定義する部分をコピペせずに済みます
- ネストしたコンポーネント構造でもロジック上ではフラットに扱えます
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | // 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 ページの定義を分割できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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 の配列です。これを使うと次の様なコンポーネントを定義できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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 の内部を持たないコンポーネントです。これを用いることで同様の動作をするコードをより簡易に書けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // 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 コンポーネントの数だけコードを変更する必要があります。置換で楽できなくもないですが事故が起きやすくもあります)。