📝

ReactコンポーネントへのPropsの渡し方/受け取り方大全

2022/03/24に公開約3,700字2件のコメント

ReactコンポーネントへPropsを渡す/受け取る方法は色々あり、意外にもまとまったリファレンスがなく説明に困ったのでまとめたいと思います。

なお、公式ドキュメントでもあまり包括的な記述がありませんが、β版のドキュメントには比較的良いページがあります:https://beta.reactjs.org/learn/passing-props-to-a-component

Propsの渡しかた

基本の渡し方

propsを渡す基本的な構文は、props名={値}です。どんな値もこの記法で渡すことができます。多くの説明書では文字列に限った渡し方から学びますが、まずはこちらが基本だと覚えたほうがわかりやすいです。

<MyComponent
  age={25}  // 数値
  rate={100 * 0.5} // 数値
  name={'John'} // 文字列
  greeting={`Hi, my name is ${name}`}  // テンプレートリテラル
  info={{ name: 'John', gender: 'male' }} // オブジェクト
  scores={[94, 98, 100]} // 配列
  name={name} // 変数
  isAdmin={false} // 真偽値
  onClose={() => {console.log()}} // 関数
 leftIcon={<Icon />} // Reactコンポーネント
  now={new Date()} // インスタンス
  wtf={null} // null
/>

このように、枚挙にいとまがありません。JavaScript的に値として評価できるものは何でも渡せます。

オブジェクトの渡し方はハマりやすいので注意

// 正しい
<MyComponent info={{ name: 'John', gender: 'male' }} />

// 誤り
<MyComponent info={ name: 'John', gender: 'male' } />

外側の波括弧はあくまでもその中身をJS的に評価するための表現であり、オブジェクトを表すものではないことに注意しましょう。

文字列の渡し方

純粋な文字列に関しては、上記の波括弧での渡し方ではなく props名="文字列"という構文を使って渡すことが多いです。ただし、変数に入った文字列や、テンプレートリテラルといったJavaScript的に評価してほしいものはこの方法では渡せません。

// 正しい
<MyComponent name="John" />

// 誤り
<MyComponent name=name /> // nameが何型であろうとこの書き方は誤り
<MyComponent greeting=`John` /> // テンプレートリテラルは{}で渡さないといけない

trueの渡し方

trueを渡す場合は、=を省略してprops名だけを書くことが可能です。

<MyComponent isActive /> 
// ↑の書き方は↓と同義。
<MyComponent isActive={true} />

falseを渡したい場合や、falseかtrueかわからない変数は=で渡す必要があります

<MyComponent isActive={false} />

const isActive = getIsActive() // booleanを返す任意の関数
<MyComponent isActive={isActive} /> // =で渡す
<MyComponent isActive /> // これは常にtrue

応用: objectを展開して渡す

JavaScriptのオブジェクトには、Spreadや、Rest Spreadという文法があります。これをコンポーネントのProps渡しで使うと、オブジェクトを展開した上で各key-valueをprops名-値として一気に渡すことができます。

const info = { name: 'John', age: 25, gender: 'male'}

<MyComponent {...info} />
// ↑の書き方は↓と同義。
<MyComponent name={info.name} age={info.age} gender={info.gender} />

Rest Spreadを使った記法もあります。

const info = { name: 'John', age: 25, gender: 'male'}
const {name, ...rest} = info

<MyComponent {...rest} /> // ageとgenderだけを渡す

Propsの受け取り方

様々なPropsの渡し方をみてきましたが、受け取り方はいたってシンプルです。なぜなら、propsはいくつ渡しても1つのオブジェクトとして受け取るので、型によってかわるこtかわるこt

基本

propsはいくつ渡しても、1つのオブジェクトです。

<MyComponent name="John" />

function MyComponent(props) {
  console.log(props.name)
  // ...
}

複数渡した場合も、1つのオブジェクトとして受け取ります。

<MyComponent name="John" age={30} />

// 正しい
function MyComponent(props) {
  console.log(props.name, props.age)
  // ...
}

// 誤り
function MyComponent(name, age) {
  console.log(name, age)
  // ...
}

Object Destructuringを使った受け取り方

これはReactというよりも、JS的にObjectを展開して利用する記法なのですが、この文法を使うとprops.nameといちいちpropsを書く必要がなくなったりして便利なときがあります。

<MyComponent name="John" age={30} />

function MyComponent({name, age}) {
  // ...
}
// ↑の書き方は↓と同義
function MyComponent(props) {
  const name = props.name
  const age = props.age
  // ...
}

Object Rest Spreadを使った受け取り方

想像に難くないと思いますが、これらはただの1オブジェクトであるPropsをどう受けとるかというJS的な表現方法の問題なので、Spread, Rest Spreadなどを使った書き方も可能です。これはButtonコンポーネントのような抽象度の高いコンポーネントで拡張性を高めるために使われる事が多いです。

// id以外の全てのPropsを子にわたす
function MyComponent({id, ...rest}) {
  console.log(id)
  return <ChildComponent {...rest}>
}

おわりに

他にあげることも可能ですが、よく見る記法のほとんどは本記事で取り上げられたのではないかと思います。もし何らかの記法を追加してほしい等の要望があれば追記したいと思います。

Discussion

propsの記述まとめて頂きありがとうございます。今後参考にいたします!

基本の渡し方にある中でFunctionオブジェクトの例がないですが、こちらは不要ですかね。

const handleToggle = () => {
    setShow(!isShow);
  };

// or 
// function handleToggle() {
//   setShow(!isShow);
// }
  return (
    <div>
      <button onClick={handleToggle} type="button">
        Toggle
      </button>
    </div>
  );
};

=========

あと一点お伺いしたいのですが、これらは関数の引数に値を渡す動作と似ていると感じました。なぜReactは()ではなく、{}にしたんですかね。実際に公式が回答していればそちらを、でなければ仮説でも良いのでお伺いできればと思います。

<MyComponent
  age={25}  // hoge(25) に似ている
  info={{ name: 'John', gender: 'male' }} // hoge({ name: 'John', gender: 'male' })に似ている
/>

// この記述でも良かったのでは?と思ってしまう
<MyComponent
  age=(25)  // hoge(25) に似ている
  info=({ name: 'John', gender: 'male' }) // hoge({ name: 'John', gender: 'male' })に似ている
/>

ご質問ありがとうございます。
そうですね、あげるとキリがない感じではありますが、変数を渡すのと同じと捉えていただければよいのかなと思いますね。

また、結局これはbabelなどのパーサがどうハンドリングするかという観点になります。おそらく()よりも{}のほうがパーサがハンドリングしやすいということだったのではないかと思います。

babelはcoreにJSXの文法のIdentifierがあり、それを元にハンドリングしtransformするplugin(下記のbabel-plugin-transform-react-jsxなど)を渡してあげることで、最終的にランタイムで動くコードになります。

https://babeljs.io/docs/en/babel-plugin-transform-react-jsx
ログインするとコメントできます