コンポーネントに props を渡す
React コンポーネントは互いにやりとりをする際に props というものを使います。親コンポーネントは子コンポーネントに props を渡すことで情報を伝えることができるのです。props は HTML の属性と似ていると思われるかもしれませんが、props ではオブジェクトや配列、関数などのあらゆる JavaScript の値を渡すことができます。
このページで学ぶこと
- コンポーネントに props を渡す方法
- コンポーネントから props を読み出す方法
- props のデフォルト値を指定する方法
- コンポーネントに JSX を渡す方法
- props は時間とともに変化する
お馴染みの props
props とは JSX タグに渡す情報のことです。例えば className
、src
、alt
、width
や height
は <img>
に渡すことのできる props の例です:
function Avatar() { return ( <img className="avatar" src="https://i.imgur.com/1bX5QH6.jpg" alt="Lin Lanying" width={100} height={100} /> ); } export default function Profile() { return ( <Avatar /> ); }
<img>
に渡すことができる props の種類は事前に決められています(ReactDOM は HTML 標準に準拠しています)。しかし <Avatar>
のようなあなた独自のコンポーネントの場合は、任意の props を渡してそれをカスタマイズできます。以下でやり方を説明します。
コンポーネントに props を渡す
以下のコードでは、Profile
コンポーネントは子コンポーネントである <Avatar>
に何の props も渡していません:
export default function Profile() {
return (
<Avatar />
);
}
以下の 2 ステップで Avatar
に props を与えることができます。
Step 1: 子コンポーネントに props を渡す
まず、Avatar
に何か props を渡します。例えば person
(オブジェクト)と size
(数値)を渡してみましょう:
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
これでこの props を Avatar
コンポーネント内から読み出せるようになります。
Step 2: 子コンポーネントから props を読み出す
これらの props を読み出すには、function Avatar
の直後の ({
と })
内で、コンマで区切って person, size
のように名前を指定します。これにより Avatar
のコード内で変数と同じようにこれらの props が使えるようになります。
function Avatar({ person, size }) {
// person and size are available here
}
Avatar
内に person
や size
を使って何かをレンダーするロジックを書き加えれば完成です。
これで Avatar
に様々な props を渡すことで様々に表示を変えられるようになりました。実際に値をいじってみましょう!
import { getImageUrl } from './utils.js'; function Avatar({ person, size }) { return ( <img className="avatar" src={getImageUrl(person)} alt={person.name} width={size} height={size} /> ); } export default function Profile() { return ( <div> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> <Avatar size={80} person={{ name: 'Aklilu Lemma', imageId: 'OKS67lh' }} /> <Avatar size={50} person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }} /> </div> ); }
props のおかげで、親と子のコンポーネントを独立して考えることができるようになります。例えば、Profile
で person
や size
を変更するときに Avatar
内でどう使われるかを気にしないでよくなります。同様に、Avatar
がこれらの props をどのように使うのかは、Profile
を見ずに変更できるようになります。
props とは自分で調整できるコントローラの「ツマミ」のようなものです。関数における引数と同じ役割を果たしています。むしろ、props があなたのコンポーネントの唯一の引数です! React コンポーネントは props
というオブジェクトを唯一の引数として受け取っているのです。
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
通常は props
オブジェクト全体を必要とすることはないため、個々の props へと分割代入します。
props のデフォルト値を指定する
props に、値が渡されなかった場合にフォールバックとして使うデフォルト値を指定したい場合、分割代入の中でパラメータ名の直後に =
とデフォルト値を書くことができます:
function Avatar({ person, size = 100 }) {
// ...
}
これで、size
プロパティを指定せずに <Avatar person={...} />
のようにレンダーされた場合、size
は 100
にセットされます。
このデフォルト値は size
がない場合や size={undefined}
を渡した場合にのみ使用されます。size={null}
や size={0}
を渡した場合にはデフォルト値は使われません。
JSX スプレッド構文で props を転送する
ときに、props の受け渡しが冗長な繰り返しになってしまうことがあります:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
繰り返しの多いコードが悪いわけではありませんし、その方が読みやすいこともあるでしょう。しかし簡潔であることに価値があることもあります。この Profile
が Avatar
に対してやっているように、コンポーネントの中には props をそのまま子コンポーネントに転送するものがあります。Profile
は props を直接的に使っているわけではありませんので、以下のような「スプレッド」構文を使って短く書く方が理にかなっているかもしれません:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
これにより、Profile
に渡された props を、個々の名前を列挙することなくすべて Avatar
に転送できます。
スプレッド構文は慎重に使ってください。この構文をあらゆるコンポーネントで使っているなら、何かが間違っています。多くの場合は、コンポーネントを分割して JSX として children を渡すべきというサインです。今からこれについて述べていきます。
children として JSX を渡す
ブラウザの組み込みタグをネストすることはよくありますね。
<div>
<img />
</div>
同様にして独自コンポーネントもネストしたくなることがあります。
<Card>
<Avatar />
</Card>
このように JSX タグ内でコンテンツをネストした場合、親側のコンポーネントはその中身を children
という props として受け取ります。例えば以下の Card
コンポーネントは、<Avatar />
がセットされた children
プロパティを受け取って、ラッパ div 要素の内部にそれをレンダーしています:
import Avatar from './Avatar.js'; function Card({ children }) { return ( <div className="card"> {children} </div> ); } export default function Profile() { return ( <Card> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> </Card> ); }
<Card>
内の <Avatar>
を何かテキストに置き換えてみて、ネストされているどんなコンテンツでも Card
コンポーネントは囲んで表示できるということを確かめてください。中に何が表示されるかあらかじめ知っておく必要はありません。このような柔軟なパターンは、様々なところで目にすることになるでしょう。
children
プロパティを有するコンポーネントには、親に任意の JSX で「埋めて」もらうための「穴」が開いている、と考えることができます。children
は、パネルやグリッドのような視覚的に何かを囲む要素に使うことができます。
Illustrated by Rachel Lee Nabors
props は時間とともに変化する
以下の Clock
コンポーネントは親コンポーネントから color
と time
という 2 つの props を受け取っています。(親コンポーネントのコードは、まだ解説していない state を使っているため省略しています。)
以下の選択ボックスでカラーを変えてみてください:
export default function Clock({ color, time }) { return ( <h1 style={{ color: color }}> {time} </h1> ); }
この例は、コンポーネントは時間経過とともに別の props を受け取る可能性があるということを示しています。props は常に固定だとは限らないのです! ここでは time
プロパティは毎秒変化していますし、color
プロパティもあなたが別の色を選択するたびに変化します。props とはコンポーネントの最初の時点ではなく、任意の時点でのコンポーネントのデータを反映するものなのです。
しかし、props はイミュータブル (immutable) です。これは「不変な」という意味のコンピュータサイエンス用語です。コンポーネントの props が(例えばユーザ操作や新しいデータの到着に応じて)変わらないといけない場合、親のコンポーネントに別の props、つまり新しいオブジェクトを渡してもらう必要があります! 古い props は忘れられ、使われていたメモリは JavaScript エンジンがそのうち回収します。
「props の書き換え」をしようとしてはいけません。(上記のカラー選択のように)ユーザの入力に反応する必要がある場合は、「state のセット」が必要です。これについては State: A Component’s Memory で学びます。
まとめ
- props を渡すには HTML で属性を書くのと同様のやり方で JSX 内に書く。
- props を読み出すには、
function Avatar({ person, size })
のような分割代入構文を使う。 size = 100
のようなデフォルト値を指定でき、これは props がない場合やundefined
の場合に使われる。<Avatar {...props} />
のような JSX スプレッド構文ですべての props を転送できるが、多様は禁物!<Card><Avatar /></Card>
のようなネストされた JSX を書くとCard
コンポーネントのchildren
プロパティになる。- props とはある時点での読み取り専用のスナップショットである。レンダー毎に新しい props のバージョンを受け取る。
- props を書き換えることはできない。インタラクティブ性が必要な場合は state を設定する必要がある。
チャレンジ 1 of 3: コンポーネント抽出
以下の Gallery
コンポーネントには 2 名のプロフィール用にとても似たマークアップが含まれてしまっています。ここから Profile
というコンポーネントを抽出してコードの重複を減らしてください。どんな props を渡すのかは自分で決める必要があるでしょう。
import { getImageUrl } from './utils.js'; export default function Gallery() { return ( <div> <h1>Notable Scientists</h1> <section className="profile"> <h2>Maria Skłodowska-Curie</h2> <img className="avatar" src={getImageUrl('szV5sdG')} alt="Maria Skłodowska-Curie" width={70} height={70} /> <ul> <li> <b>Profession: </b> physicist and chemist </li> <li> <b>Awards: 4 </b> (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal) </li> <li> <b>Discovered: </b> polonium (element) </li> </ul> </section> <section className="profile"> <h2>Katsuko Saruhashi</h2> <img className="avatar" src={getImageUrl('YfeOqp2')} alt="Katsuko Saruhashi" width={70} height={70} /> <ul> <li> <b>Profession: </b> geochemist </li> <li> <b>Awards: 2 </b> (Miyake Prize for geochemistry, Tanaka Prize) </li> <li> <b>Discovered: </b> a method for measuring carbon dioxide in seawater </li> </ul> </section> </div> ); }