React開発を始めたばかりの皆さん、コンポーネント間でデータを受け渡しする「Props」について、難しく感じていませんか?
Propsは、Reactの基本でありながら、その仕組みや「読み取り専用」というルールが少し複雑に思えるかもしれません。しかし、Propsは親コンポーネントが子コンポーネントにデータを渡すだけなので、一度理解すれば、コンポーネントの再利用性が格段に向上します。
この記事では、Propsの基本概念から、親から子へデータを渡す具体的な手順や注意点を、実例のコードを用いて段階的にわかりやすく解説しています。
Propsとは何か?
Propsとは、属性を意味するプロパティの複数形「properties」の略で、親コンポーネントから子コンポーネントへデータを渡すための仕組みです。(Reactでは、ウェブサイトの部品を「コンポーネント」という単位で作ります。)
Propsを使うことで、親が子の見た目や中身を「〇〇という内容で表示してね」「〇〇という大きさにしてね」と指定できるようになり、子コンポーネントを様々な状況で使い回す(再利用する)ことができます。
Reactのコンポーネントとは何か?については下記記事をご参考ください。
Propsの基本と特徴
Propsを使う上で絶対に覚えておくべき特徴が2つあります。
- データの流れは「一方向」(親 → 子)
- Propsは「読み取り専用」(変更不可)
- propsという属性があるわけではない
データの流れは「一方向」(親 → 子)
Propsのデータは、親コンポーネントから子コンポーネントへと、常に一方向に流れます。
- 親は子にデータを渡せます。
- 子は親から渡されたデータを使うことができます。
※子から親にデータを渡すことはできません。
Propsは「読み取り専用」(変更不可)
子コンポーネントは、受け取ったPropsの値を直接変更することはできません。不変(イミュータブル)という性質を持ちます。
データの流れをシンプルにし、アプリケーションの動作を予測しやすくするために、このルールがあります。
もしコンポーネント内でデータを変更したい場合は、State(ステート:コンポーネントが内部で管理する「状態」)という別の仕組みを使います。
propsという属性があるわけではない
Propsはpropsという専用の属性があるわけではありません。
子コンポーネントは、親から渡されたすべてのカスタム属性を一つのJavaScriptオブジェクトとして受け取ります。この受け取ったオブジェクト全体を慣習的にPropsと呼びます。
親から渡されたカスタム属性を受け取る際の変数を慣習的に「props」を使います。(変数名は任意です)
Propsの使い方
Propsを使う場合は①子コンポーネントで受け取ったPropsを呼び出す記述と、②親コンポーネントで子コンポーネントにPropsを渡す記述が必要になります。
子コンポーネントの書き方
子コンポーネントを定義した際に、引数で親から渡されたデータ(Props)を受け取ることができます。
その際、引数名は「props」を使うことが一般的です。
受け取ったデータはドット記法(プロパティアクセス)で呼び出します。
{{ props.プロパティ名 }}JSX内でプロパティを扱うときは、プロパティであることを伝えるために二重カッコを使います。
例えば、以下のように記述します。
const コンポーネント名 = (props) => {
return (
<p style={{ backgroundColor: props.color }}>
{props.label}
</p>
);
};親コンポーネントの書き方
親コンポーネントから子コンポーネントにデータを渡すときは、子コンポーネントを呼び出したタグの中で、属性を設定します。
<子コンポーネント名 属性A="値A" 属性B="値B"...>Propsの実例
まずは子コンポーネントを作成します。
ここでは例としてButtonコンポーネントを作成します。
const Button = (props) => {
return (
<button style={{ backgroundColor: props.color }}>
{props.label}
</button>
);
};
export default Button;これで、親コンポーネントかわ渡された属性「color」と「label」の値を受け取ることができます。
親コンポーネントは次のように記述します。
import Button from './Button.jsx'
const App = () => {
return (
<div>
<Button label="詳細はこちら" color="blue" />
<Button label="キャンセル" color="gray" />
</div>
);
};分割代入で受け取る
受け取る際に分割代入を使うと、コードがより簡潔になり、何を受け取るか分かりやすくなります。一般的によく使われる方法です。
分割代入とは、配列やオブジェクトから、その要素やプロパティを取り出して、個別の変数に代入することを可能にする便利な構文です。
例えば、親コンポーネントから子コンポーネントにlabelとcolorを渡すとします。
import Button from './Button.jsx'
const App = () => {
return (
<div>
<Button label="詳細はこちら" color="blue" />
<Button label="キャンセル" color="gray" />
</div>
);
};
分割代入で受け取るには、引数で属性名を直接記述します。
const Button = ( { label, color } ) => {
return (
<button style={{ backgroundColor: color }}>
{label}
</button>
);
};
export default Button;
こうすることでオブジェクト名.プロパティ名というドット記法を使う必要がなく、親から渡されたPropsのうち「label」と「color」プロパティのみを受け取り、そのまま変数に代入することができます。
デフォルト値の設定
子コンポーネントでPropsを使う際に、親コンポーネントからデータが渡されていないとエラーが発生します。
これを防ぐために、値が渡されなかった場合にデフォルト値を指定することができます。
分割代入で変数に値を格納する場合、=でフォルフォル値を指定します。
const Button = ( { label="もっと見る", color='#333' } ) => {
return (
<button style={{ backgroundColor: color }}>
{label}
</button>
);
};
export default Button;Propsで渡せるデータ
ほとんどすべてのJavaScriptの値(データ)をPropsとして渡すことができます。
| データ型 | 親での渡し方 | 子での使い方(分割代入の場合) |
| 文字列 | <Component text="Hello!" /> | const Component = ({ text }) => <h1>{text}</h1>; |
| 数値 | <Component count={5} /> | const Component = ({ count }) => <p>{count}</p>; |
| 真偽値 | <Component isActive={true} /> | const Component = ({ isActive }) => (isActive ? 'ON' : 'OFF'); |
| オブジェクト | <Component user={{ name: 'Taro' }} /> | const Component = ({ user }) => <p>{user.name}</p>; |
| 関数 | <Component onClick={myFunc} /> | const Component = ({ onClick }) => <button onClick={onClick}>Go</button>; |
スプレッド構文の活用(親から孫にデータを渡す)
親コンポーネントから子コンポーネントを経由して孫コンポーネントにデータを渡すとき、分割代入で記述するとコードが冗長になってしまうことがあります。
例えば、子コンポーネントProfileで複数のプロパティを受け取り、それを孫コンポーネントAvatorに渡す場合は以下のようになります。
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
スプレッド構文を使うと、より簡潔に記述することができます。
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}コードの解説
仮に、親コンポーネントから子のProfileコンポーネントに以下のデータが渡されているとします。
//親コンポーネント
<Profile person="Aさん" size="L" isSepia=true thickBorder=false />この場合、子のProfileコンポーネントのpropsには以下が入ります。
props = { person:"Aさん", size:"L", isSepia:true, thickBorder:false }スプレッド構文を使うことで、孫コンポーネントにpropsをまるごと渡すことができます。
<Avatar {...props} />上記のスプレッド構文はJavaScriptの動きとはことなるJSX専用の動きをします。
JavaScriptの場合、{…props}と記述すると、呼び出されるのは以下のコードです。
{ person:"Aさん", size:"L", isSepia:true, thickBorder:false }ですが、JSXの場合は属性として呼び出されます。
person="Aさん" size="L" isSepia=true thickBorder=falseこれは次のような展開がなされているためです。
<Avatar {...props} />
↓
<Avatar person={props.person} size={props.size} isSepia={props.isSepia} thickBorder={props.thickBorder} />スプレッド構文のより詳しい使い方は下記記事をご参考ください。
> 【JavaScript】ドット3つ「…」とは何か?スプレッド構文の使い方を実例で解説|エラー対処法:error: Unexpected token ‘…’
Propsの注意点
Propsを使う際は以下の点に注意が必要です。
- 変更してはいけない
- 遠い孫コンポーネントに渡す場合(プロップ・ドリリング)
- children Props
変更してはいけない
Propsは親→子の一方向なので、子コンポーネントで変更してはいけません。
//子コンポーネント
const User = ({ name }) => {
name = 'Jiro'; //エラー!
return <h1>{name}</h1>;
};遠い孫コンポーネントに渡す場合(プロップ・ドリリング)
親から遠い孫コンポーネントなどにPropsの値を渡したい場合、間にいるすべての子コンポーネントが、そのPropsをただ受け取って下に渡すだけの役割を担うことがあります。(Prop Drilling(プロップ・ドリリング)と呼びます)
階層が深くなると、コードが読みにくく、メンテナンスしづらくなるという問題があります。
この問題の解決策としては、ReactのContextや、Reduxなどの状態管理ライブラリの使用が検討されます。
children Props
子コンポーネントを定義する際に、開始タグと終了タグの間に挟まれた要素は、自動的にchildrenという名前のPropsとして渡されます。
例えば、親コンポーネントの中で子コンポーネントを呼び出したときに、子コンポーネントタグのにタグを定義します(以下ではh1とp)。
//親コンポーネント
import Card from './Card.jsx';
const App() =>{
return(
<Card>
<h1>見出し</h1>
<p>この部分は子要素として渡されます。</p>
</Card>
);
}
このh1とpタグを子コンポーネントに渡すことができます。
const Card = ( props ) => {
return (
<div style={{ border: '1px solid black', padding: '10px' }}>
{/* children がタグに挟まれた要素を受け取る */}
{props.children}
</div>
);
};↓ 分割代入の場合は以下のようになります。
const Card = ({ children }) => {
return (
<div style={{ border: '1px solid black', padding: '10px' }}>
{/* children がタグに挟まれた要素を受け取る */}
{children}
</div>
);
};children Propsはデータをは渡すときにタグが複数ある場合は配列で渡します。
//上記例でCardコンポーネントが受け取るデータ
{ children: [<h1>, <p>] }タグが1つの場合はそのタグのみを返します。
//上記例でCardコンポーネントが受け取るデータ
{ children: <h1> }

