Reactでコンポーネントを他のコンポーネントに渡せるpropsのchildrenの使い方を紹介します。
childrenを上手に使うと、再利用性の高いコンポーネントを作成できます。
結論から言うと、childrenは「開始タグと終了タグの間に書いたもの」が自動で入る特別なpropsです。受け取る側は({ children }) => <div>{children}</div>のように分割代入で取り出し、好きな場所に{children}として置くだけで使えます。文字列・JSX・配列・別のコンポーネントなど、JSXの中に書けるものなら何でも渡せます。TypeScriptではchildren: React.ReactNodeと型を付けます。
childrenの基本的な使い方
他のコンポーネントをラップするコンポーネント(親)
import ChildComp from "./components/ChildComp";
import Container from "./components/Container";
const profile = [
{ name: "yamada", age: 78, country: "KOREA" },
{ name: "suzuki", age: 45, country: "USA" },
];
const Func = () => {
return (
<div>
<Container title="Childrenについて" >
{'children'}
<ChildComp {...profile[0]} />
<ChildComp {...profile[1]} />
</Container>
</div>
);
};
export default Func;
このように、コンポーネントの中にコンポーネントを入れると、
propsと一緒にコンポーネントもpropsとして渡せます。
オブジェクトの配列にしておけば、いろいろなデータを出力できます。
{...profile[0]}のようにオブジェクトをまとめてpropsにする書き方は配列の中のオブジェクトのプロパティすべてをpropsとして渡す方法で解説しています。
また、childrenは属性として設定することもできます。
const profile = [
{ name: "yamada", age: 78, country: "KOREA" },
{ name: "suzuki", age: 45, country: "USA" },
];
const Func = () => {
return (
<div>
<Container
title="Childrenについて"
children={
[(<ChildComp key={profile[0].name} {...profile[0]} />), (<ChildComp key={profile[1].name} {...profile[1]} />)]
}
></Container>
</div>
);
};
export default Func;
その場合、コンポーネントChildCompもJavaScriptのオブジェクトのため、上のように渡せます。
ただし、この場合はkey属性でユニークな値を指定する必要があります(指定しないと警告が出ます)。
よって、上のようにユニークな値のkeyを指定します。
なお、属性としてchildren={...}と書く方法は動作はしますが、React公式は推奨していません。通常はタグの間に書く形を使い、属性の書き方は「childrenも普通のpropsの1つ」と理解するための例と捉えてください。
const Container = ({ title, children }) => {
console.log(children);
return (
<div className="container">
<h3>{title}</h3>
{children}
</div>
);
};
export default Container;
上のように、コンポーネントはchildrenという名前の引数で渡ってくるので、
{}で使えます。JSXの{}に書けるのは式だけという点はJSX内にJavaScriptの式を埋め込む方法を参考にしてください。
childrenとしてラップされる側のコンポーネント(子)
const ChildComp = ({ name, age, country }) => {
return (
<div>
<h3>Name: {name}</h3>
<p>Age: {age} </p>
<p>Country: {country} </p>
</div>
);
};
export default ChildComp;
こちらは通常と同じです。
渡すコンポーネントの順番を指定する方法
ラップする側の親コンポーネント
import ChildComp from "./components/ChildComp";
import Container from "./components/Container";
const profile = [
{ name: "yamada", age: 78, country: "KOREA" },
{ name: "suzuki", age: 45, country: "USA" },
];
const Func = () => {
return (
<div>
<Container
title="Childrenについて"
children={[
<ChildComp key={profile[0].name} {...profile[0]} />,
<ChildComp key={profile[1].name} {...profile[1]} />,
]}
zero={<ChildComp key={profile[0].name} {...profile[0]} />}
first={<ChildComp key={profile[1].name} {...profile[1]} />}
></Container>
</div>
);
};
export default Func;
コンポーネントの順番を制御したい場合があります。
そのようなときは、上のように、それぞれのコンポーネントにそれぞれの属性をつけることで、可能になります。
ラップされる側の子コンポーネント
const Container = ({ title, children, zero, first }) => {
console.log(children);
return (
<div className="container">
<h3>{title}</h3>
{children}
{first}
{zero}
</div>
);
};
export default Container;
上のようにしておけば、{zero}と{first}の順番を入れ替えて、表示するコンポーネントの順番を制御できます。
childrenが1つだけのときは要素そのもの、複数あるときは配列として渡ってきます。数を数えたり1つずつ加工したりする場合は、どちらでも同じ扱いができるReact.Children.map(children, fn)やReact.Children.count(children)を使います。
よくある実例-再利用可能なカードコンポーネント
ラップする側の親コンポーネント
import "./card.css";
const Card = ({ children, className, onClick }) => {
return (
<article className={`card ${className}`} onClick={onClick}>
{children}
</article>
);
};
export default Card;
上のように、childrenのほかにclassNameを設定しておくと、たとえばaboutページとprofileページで同じカードを使いつつ、クラス名だけを変えて再利用できます。
また、onClickは渡されなければundefinedになるだけなので、onClickがある場合もない場合も上の書き方で問題ありません。
ラップされる側の子コンポーネント
import Card from "../../components/Card";
const data = [
{id: 1, icon: <Icon1/>, title: 'タイトル1', desc: 'desc1'},
{id: 2, icon: <Icon2/>, title: 'タイトル2', desc: 'desc2'},
{id: 3, icon: <Icon3/>, title: 'タイトル3', desc: 'desc3'}
]
const About = () => {
return (
<section id="about">
<div className="container about__container">
<h2>About Me</h2>
<div className="about__cards">
{data.map((item) => (
<Card key={item.id} className="about__card">
<span className="about__card-icon">{item.icon}</span>
<h5>{item.title}</h5>
<small>{item.desc}</small>
</Card>
))}
</div>
<p>
ここに文章
</p>
<a href={CV} download className="btn primary">
ダウンロード
</a>
</div>
</section>
);
};
export default About;
上のようにCardコンポーネント内で記述されたものがchildrenとして渡されます。
また、CardコンポーネントにclassNameをつけることで、静的な書き方であっても当然propsとしてclassNameが渡されます。
あとは、他のコンポーネントでも同じように、Cardコンポーネント内の記述とclassNameと、必要に応じてonClickを設定すれば、
再利用可能なカードコンポーネントになります。
childrenでよくあるつまずき
childrenが表示されない: 受け取る側で{children}を置き忘れているか、propsの分割代入の名前がchildren以外になっています。名前は固定です- 配列を渡したら「Each child in a list should have a unique key」の警告: 配列の各要素に
keyを付けます。タグの間に並べて書いた場合は不要です - TypeScriptで型エラー:
type Props = { children: React.ReactNode }のように宣言します。JSX.Elementだと文字列や配列が渡せなくなります
childrenを使って開閉するパネルを組み立てる実例はReactでアクセシブルなアコーディオンを作る方法で紹介しています。