React入門第5回~Propsでコンポーネントに値を渡そう~

Propsでコンポーネントに値を渡そう

OZ
OZ

プロ太先生~。今日はReactのコンポーネントにPropsを使ってデータを渡す方法についての授業ですよね?よろしくお願いします!!

プロ太
プロ太

はい、OZくん。Propsはコンポーネントに情報を渡すための仕組みです。これによって、コンポーネントの見た目や動作を柔軟に変更することができるようになります。まず、こんなコンポーネントがあるとしましょう。

import "./ColorComponent.css";

const ColorComponent = (props) => {
  return (
    <div className={`color-box ${props.colorClass}`}>
      <h3>This is a color box</h3>
    </div>
  );
};

export default ColorComponent;

ここではPropsのcolorClassを使って、divタグのクラス名を動的に変更しています。これによって、コンポーネントの色を変えることができます。

関数の引数としてpropsという変数が受け取られています。propsには、ColorComponentを使う側で渡されたデータが格納されます。例えば、colorClassというPropsを渡せば、props.colorClassでその値にアクセスできるようになります。

実際にColorComponentを使う側のコードを見てみましょう。

import ColorComponent from "./ColorComponent";

const App = () => {
  return (
    <div>
      <ColorComponent colorClass="blue" />
      <ColorComponent colorClass="red" />
      <ColorComponent colorClass="green" />
    </div>
  );
};

export default App;

ここでは、3つのColorComponentを使っています。
それぞれにcolorClassというPropsを渡しています
<ColorComponent colorClass="blue" />の場合、props.colorClassの値が"blue"になります。
同様に、"red""green"が渡されています。

そしてColorComponent.cssには以下のようなスタイルを定義します。

.color-box {
  padding: 20px;
  border-radius: 8px;
  text-align: center;
}

.color-box.blue {
  background-color: #ADD8E6;
  color: #1E90FF;
}

.color-box.red {
  background-color: #FF6961;
  color: #DC143C;
}

.color-box.green {
  background-color: #90EE90;
  color: #006400;
}

なるほど!Propsでクラス名を動的に渡せると、デザインが自由に変えられるんですね。

分割代入で書いてみよう

そしてさらに、JavaScriptの「分割代入」という機能を使うと、もっと簡単に書けます。例えば、今は(props)と書いていますが、これを({ colorClass })と書き換えることができます

const ColorComponent = ({ colorClass }) => {
  return (
    <div className={`color-box ${colorClass}`}>
      <h3>This is a color box</h3>
    </div>
  );
};

こうすると、propsというオブジェクト全体を受け取る代わりに、必要なcolorClassだけを直接受け取ることができるんです。これでコードがすっきりして、何が渡されているのか一目でわかりやすくなりますね!

なるほど、分割代入って便利ですね!じゃあ、もし他のPropsも渡したい場合はどうなるんですか?

いい質問です。では、新しくsizeというPropsを追加して、色だけでなく大きさも変えたいとしましょうsizeには "small""large" などの値を渡して、コンポーネントの見た目を調整できますよ。コードを見てみましょう。

import "./ColorComponent.css";

const ColorComponent = ({ colorClass, size }) => {
  return (
    <div className={`color-box ${colorClass} ${size}`}>
      <h3>This is a color box</h3>
    </div>
  );
};

export default ColorComponent;

これで、sizeというPropsも受け取れるようになりました。次に、Appコンポーネント側で、ColorComponentsizeを渡してみましょう。

import ColorComponent from "./components/Child";

const App = () => {
  return (
    <div>
      <ColorComponent colorClass="blue" size="small" />
      <ColorComponent colorClass="red" size="large" />
      <ColorComponent colorClass="green" size="small" />
    </div>
  );
};

export default App;

これで、各ColorComponentに対して色だけでなく、大きさも指定できます。CSS側でもサイズに応じたスタイルを追加します。ちなみにCSSは以下のようにしています。

.color-box {
  padding: 20px;
  border-radius: 8px;
  text-align: center;
}

.color-box.blue {
  background-color: #add8e6;
  color: #1e90ff;
}

.color-box.red {
  background-color: #ff6961;
  color: #dc143c;
}

.color-box.green {
  background-color: #90ee90;
  color: #006400;
}

.color-box.small {
  width: 50%;
  height: 100px;
}

.color-box.large {
  width: 90%;
  height: 200px;
}

なるほど、Propsを増やしてどんどんカスタマイズできるんですね。これなら、このsizecolorClassみたいに、必要な値が増えてきても、すっきりしたコードのままでいられますね。分割代入、便利です。でも、ここで復習しておきたいです!

分割代入を復習しておこう

そうですね。では、ここで少し復習として、分割代入の仕組みについて整理しておきましょう。分割代入は、オブジェクトや配列の中身を簡単に取り出すための便利な構文です。ReactのPropsでも使えますが、まずはシンプルな例で見てみましょう。

const user = {
  name: "OZ",
  age: 17,
  club: "badminton",
};

このuserオブジェクトからnameageだけを取り出したいとき、通常ならこう書きます。

const name = user.name;
const age = user.age;

これでも取り出せますが、分割代入を使うともっと短く、こんな風に書けます。

const { name, age } = user;

ここで、const { name, age } = user;と書くと、「userの中からnameageというデータだけを取り出して、個別に変数として使えるようにする」という意味になります。

具体的には、「name」と「age」だけを取り出して、それぞれ別の小箱に入れるような感じです。

const name = "OZ"; // userから取り出されたname
const age = 17;    // userから取り出されたage

では、この分割代入のイメージを使って、Propsでも同じように考えてみましょう。
たとえば、先ほどのColorComponentに渡されるPropsを思い出してください。

ColorComponentは、colorClasssizeという2つのデータをPropsとして受け取ります。

<ColorComponent colorClass="blue" size="large" />

このとき、ReactはColorComponentに渡されたデータを一つのオブジェクトとしてまとめ、関数の引数propsに詰めて渡しています。propsはこんな箱のような状態です。

props
├── colorClass: "blue"
└── size: "large"

通常なら、この中のcolorClasssizeにアクセスするために、props.colorClassprops.sizeと書きます。でも、分割代入を使うと、わざわざprops.と書かなくても、直接値を取り出せるんです!

分割代入を使った書き方

const ColorComponent = ({ colorClass, size }) => {
  return (
    <div className={`color-box ${colorClass} ${size}`}>
      <h3>This is a color box</h3>
    </div>
  );
};

ここで、({ colorClass, size })と書くことで、propsという箱からcolorClasssizeだけを抜き出し、直接使える状態にしています。イメージ的には、Propsからそれぞれのデータを取り出して、別々の小箱に分ける感じです。

const colorClass = "blue"; // propsから取り出されたcolorClass
const size = "large";      // propsから取り出されたsize

こうすることで、propsの中身を使うときにprops.と書かずに済むので、コードがすっきりして読みやすくなりますね!

Propsにいろんな種類の値を渡してみよう

OZくん、さっきのcolorClassの例でPropsの使い方は少し理解できたかな?
ちなみに、Propsは、文字列だけじゃなくて、数値や関数、ブール値、オブジェクトなど、いろいろな種類の値を渡すことができるんです。実際に以下のような画面を作る具体的なコードで見てみましょう。

import "./ColorComponent.css";

// Propsの分割代入とデフォルト値の設定
const ColorComponent = ({
  colorClass = "green",       // デフォルトで "green" を設定
  num,
  fn,
  isVisible = false,          // デフォルトで false を設定
  user = { name: "NoName", age: 0 }, // デフォルトのオブジェクト
}) => {
  return (
    <div className={`color-box ${colorClass}`}>
      <h3>This is a Color Box</h3>
      <p>Number: {num}</p>
      <p>Message: {fn("こんにちは!")}</p>
      <p>Visibility: {isVisible ? "Visible" : "Hidden"}</p>
      <p>User: {user.name}, Age: {user.age}</p>
    </div>
  );
};

export default ColorComponent;

うわ!長いコードになってきましたね。それぞれのPropsの役割を教えてください!

もちろんです!ここではPropsを分割代入で受け取り、それぞれにデフォルト値を設定しています。

  • colorClass:親コンポーネントから渡される色の名前です。colorClassが渡されなければデフォルトで"green"になります。
  • num:数値を受け取るPropsです。ここには数値を直接渡すことができます。
  • fn:関数を受け取るPropsです。親コンポーネントから関数を渡すことで、子コンポーネント内で関数を呼び出せるようになります。
  • isVisible:ブール値を受け取るPropsで、trueまたはfalseのいずれかになります。デフォルトでfalseになっていて、渡されればその値を使います。
  • user:オブジェクトを受け取るPropsで、ここではnameageを持つオブジェクトを想定しています。親からオブジェクトが渡されなければ、{ name: "NoName", age: 0 }というデフォルトのオブジェクトが使われます。

なるほど!Propsにデフォルト値を設定すると、親からの値がなくてもエラーにならずに使えるんですね!

そうなんです。デフォルト値のおかげで、コンポーネントが安定して動作します。
例えば、Appコンポーネントで以下のようにPropsを渡してみましょう。

import ColorComponent from "./ColorComponent";

const App = () => {
  const greet = (text) => {
    return `挨拶するね! ${text}!`;
  };

  return (
    <div>
      <ColorComponent 
        colorClass="blue" 
        num={123} 
        fn={greet} 
        isVisible={true} 
        user={{ name: "OZ", age: 17 }} 
      />
    </div>
  );
};

export default App;

このコードだと、Propsが渡されるのは次のような形ですね!

  • colorClass"blue"
  • num123
  • fn:greet関数
  • isVisibletrue
  • user{ name: "OZ", age: 17 }

その通りです!これで、ColorComponent内でそれぞれのPropsを使って表示したり、動作させたりできます。デフォルト値も設定してあるので、親から渡されなかった場合でも問題なく動作します。

なるほど!理解できました!ただ、渡すpropsの値が多いと少しコードが読みにくい感じはしますよね、、、。

スプレッド構文でPropsを渡す

その言葉待っていました!実はスプレッド構文を使うと、オブジェクトの内容を一度に展開してPropsとして渡すことができ、コードを読みやすくできますよ。たとえば、以下のように書けます。

import ColorComponent from "./ColorComponent";

const App = () => {
  const user = { name: "OZ", age: 17 };
  const componentProps = {
    colorClass: "blue",
    num: 123,
    fn: (text) => `挨拶するね! ${text}!`,
    isVisible: true,
    user,
  };

  return (
    <div>
      <ColorComponent {...componentProps} />
    </div>
  );
};

export default App;

{...componentProps}でPropsをまとめて渡しているんですね!便利!

その通り!このスプレッド構文を使うことで、Propsが多い場合もコードがすっきりして、まとめて管理しやすくなります。コンポーネントに渡す内容を一箇所にまとめておけるので、修正もしやすくなりますね。

Propsの重要なポイント:読み取り専用と一方通行

OZくん、最後にPropsに関する大事なポイントを話しておきます。ReactのPropsは「読み取り専用」であり、「親から子へ一方通行」という特徴を持っています。
つまり、Propsとして渡されたデータは子コンポーネントの中で変更することができないんです。

ええ、どういうことですか?

じゃあ、例を見てみましょう。

const Hello = (props) => {
  // POINT propsは読み取り専用
  // props.name = 'Bob';
  // エラーが発生!

  return (
    <div>
      <h3>Hello {props.name}</h3>
    </div>
  );
};

export default Hello;

ここでは、props.nameに新しい値を代入しようとするとエラーが発生しています。Reactの設計上、Propsの値は変更できないように守られているんです。これはコンポーネント間のデータフローを安定させるための重要なルールなので覚えておきましょう!!

Propsは親から渡されるだけで、子から変更することはできないんですね!了解です!

これによって、コンポーネントの役割が明確になって、予期しないデータの変更が防止できるんです。これはReactの「一方向データフロー」の考え方といって、データの流れが親から子にだけ伝わる仕組みになっているんです。

特別なプロパティ:props.childrenを使いこなそう!

最後に、props.childrenについて説明しますね。もう少しです!頑張ってください!

はい!頑張ります!

props.childrenは、特別なプロパティで、コンポーネントの中に他の要素を「挟む」ことで渡せます。たとえば、<Greeting>というコンポーネントを作ったとします。このGreetingの中にメッセージや他の要素を自由に挿入できるようにします。

要素を「挟む」ってどういうことですか?

具体例を見てみましょう!まず、以下のようにGreetingというコンポーネントを作ります。

const Greeting = (props) => {
  return (
    <div>
      <h2>Welcome!</h2>
      <div>{props.children}</div>
    </div>
  );
};

export default Greeting;

このGreetingコンポーネントは、props.childrenというプロパティを使って、呼び出し元から渡された要素を受け取る仕組みになっています。次に、このGreetingを使ってみよう。

<Greeting>
  <p>こんにちは!これがGreetingコンポーネントの内部に表示される内容です。</p>
</Greeting>

<Greeting>の中に<p>タグを挟んでいますね。もしかして、こうやって挟むだけで、props.childrenにその内容が渡されるんですか?

その通り!この場合、props.childrenには<p>こんにちは!これがGreetingコンポーネントの内部に表示される内容です。</p>が入ります。この結果、Greetingコンポーネントは以下のようにレンダリングされますよ。

<div>
  <h2>Welcome!</h2>
  <div>
    <p>こんにちは!これがGreetingコンポーネントの内部に表示される内容です。</p>
  </div>
</div>

なるほど!props.childrenを使うと、コンポーネントの内容を外から自由に変更できるんですね。便利です!!でも、普通のpropsとprops.childrenもどっちも何かを受け渡しに使うことはわかったんですが、2つの違いがちょっとわかりません、、、。

いい質問ですね、では、propsprops.childrenにはそれぞれ役割があるから、その違いを整理しておきましょう!!

propsについて

まず、propsはコンポーネントに特定の「データ」や「設定」を渡すために使われます。たとえば、Greetingコンポーネントに「名前」を渡したいときは、nameというpropsを設定するんです。

const Greeting = (props) => {
  return <h2>Hello, {props.name}!</h2>;
};

そして、Greetingを使うときにnameプロパティで値を渡します。

<Greeting name="OZ" />

なるほど、props.nameを使って「OZ」という名前が渡されるんでしたね!propsはこうやって設定を渡す感じでした!分割代入もできましたよね!

props.childrenについて

一方で、props.childrenは「コンポーネントの中に挟む要素」を受け取る特別なpropsです。たとえば、MessageBoxというコンポーネントを作って、その中にメッセージや他の要素を自由に挿入できるようにします。

const MessageBox = (props) => {
  return (
    <div className="message-box">
      <h3>メッセージ:</h3>
      <div>{props.children}</div>
    </div>
  );
};

そして、MessageBoxを使うときに中に要素を挟みます。

<MessageBox>
  <p>これは重要なお知らせです!</p>
</MessageBox>

この場合、props.childrenには<p>これは重要なお知らせです!</p>が入るんでしたね!

propsprops.childrenの違いを整理しよう!

なるほど、なんとなく違いが見えてきました!僕なりに、propsprops.childrenの違いをまとめてみますね!!

  1. props: コンポーネントに「設定」や「データ」を渡すためのもの。属性として渡す値を指す。
    • 例: <Greeting name="OZ" />
    • props.nameで「OZ」を取得
  2. props.children: コンポーネントの「中に挟む要素」を受け取るための特別なpropsprops.childrenは、任意のタグや他のコンポーネントをそのまま渡せる

例)

<MessageBox>
  <p>これは重要なお知らせです!</p>
</MessageBox>

props.children<p>これは重要なお知らせです!</p>を取得

すばらしいまとめです!!成長しましたね!!先生は嬉しいです。

まとめ

Propsを使うと、コンポーネントにさまざまなデータを柔軟に渡し、見た目や動作をカスタマイズできます。分割代入を用いて、必要なデータをすっきりと受け取ることができ、コードの読みやすさも向上します。また、デフォルト値を設定することで、親からのデータがない場合でも安定した動作が可能です。Propsの活用方法を理解し、Reactのコンポーネント設計の幅を広げていきましょう!