【React】 コンポーネントに子要素を入れる
多くのコンポーネントでは、基本的な<ComponentName hoge={fuga} />
のようにコンポーネントを利用してきました。ここでは、普通のDOMの要素のように、コンポーネントに子要素を持たせてみます。
シンプルな実装
まずはシンプルな実装の例を確認してみます。
受け取る側の実装
子要素を受け取って、その要素を表示するコンポーネントを作成します。
受け取る側はpropsで受け取り、children
というキーに中身が入っています。
export default function SampleComponent(props) { return ( <> <p>SampleComponent</p> <div> {props.children} </div> </> ); }
呼び出し側の実装
渡す側では、コンポーネントの子要素としてその内容を入れます。
import SampleComponent from './components/SampleComponent'; export default function App() { return ( <> <SampleComponent> <p>Child</p> </SampleComponent> </> ); }
複数の要素を渡す
上の例では、子要素が1つだけでした。複数の子要素を渡すと、渡された側のChildren
には配列でそれぞれの要素が入ります。
<SampleComponent> <p>Child</p> <p>Child2</p> <p>Child3</p> </SampleComponent>
export default function SampleComponent(props) { console.log(props); // ... }
children: Array(3) 0: {$$typeof: Symbol(react.element), type: 'p', key: null, ref: null, props: {…}, …} 1: {$$typeof: Symbol(react.element), type: 'p', key: null, ref: null, props: {…}, …} 2: {$$typeof: Symbol(react.element), type: 'p', key: null, ref: null, props: {…}, …} length: 3
これらの要素を、それぞれ区別して受け取りたい場合はどのようにするのが良いのでしょうか。
保守性が高い良い方法が無いため、基本的に子要素は1つにするべきです。
複数になりそうな場合は、コンポーネントを分割する、高階コンポーネントを検討するなど、別の方法を考えるべきです。
分割代入で受け取る
配列は分割代入を行うことで変数名を付けることができます。それをりようして最初から名前がついた状態にします。
export default function SampleComponent({children: [c1, c2, c3]}) { return ( <> <p>SampleComponent</p> <div> {c1} {c2} {c3} </div> </> ); }
上の例では、まずpropsを分割代入で受け取り、その中のchildren
を更に分割代入しています。
これのデメリットは、子要素の順序をコンポーネントから制御できないということです。子要素の並べ順を間違うと、意図した動作にできない可能性がありますが、この子要素の順番を制限することができません。
そのため、保守性の面で良いとはいえません。
特定の属性を付ける
Reactでは、配列で要素を描画する際はkey
要素を用いました。ここでもそれを用いて、受け取る側で対応するkey
で検索します。
<SampleComponent> <p key="c1">Child</p> <p key="c2">Child2</p> <p key="c3">Child3</p> </SampleComponent>
export default function SampleComponent(props) { const c1 = props.children.find((v) => v.key === "c1"); const c2 = props.children.find((v) => v.key === "c2"); const c3 = props.children.find((v) => v.key === "c3"); return ( <> <p>SampleComponent</p> <div> {c1} {c2} {c3} </div> </> ); }
これのデメリットは、実装が面倒であることです。要素を取得するためにわざわざ配列から検索して取り出す実装を書く必要があります。しかし、子要素の順番は何でも良いというメリットがあります。
そもそも属性にJSXを入れる
子要素としてではなく、属性としていれる方法です。
<SampleComponent c1={<p>Child</p>} c2={<p>Child2</p>} > <p key="c3">Child3</p> </SampleComponent>
export default function SampleComponent(props) { return ( <> <p>SampleComponent</p> <div> {props.children} {props.c1} {props.c2} </div> </> ); }
この場合は受け取る側は普通にprops
から取り出すだけなので楽に受け取れます。
しかし、呼び出す側では属性の値にDOMを入れるようなものなので、書きやすさは劣り、可読性も低くなります。直感的ではありません。
まとめ
コンポーネントに子要素を設定することで、呼ばれたコンポーネント側ではprops.children
でその要素にアクセスできます。表示する際は{props.children}
のように書くだけです。
複数の子要素を入れる場合は面倒事が多いため、それぞれの子要素の区別が必要ない形で扱うか、そもそもコンポーネントを分けるなどの対応を取ることを推奨します。
