こんにちは、かつコーチです。
前回は仮想DOMの仕組みを見ながら、Reactがなぜ速いのかを解説しました。
今回は、React学習で最初につまずきやすいポイントの一つ「Props」を扱います。
コンポーネントに値を渡すための仕組みなのですが、最初は「なぜ必要なのか」がピンとこない人が多いです。
具体例を交えながら、順番に理解していきましょう。
Propsとは?
親コンポーネントから子コンポーネントに渡す「引数」
Propsとは、親コンポーネントから子コンポーネントに値を渡すための仕組みです。
「プロパティ(Property)」の略で、コンポーネントを関数だと考えると、Propsは「その関数に渡す引数」に近いイメージです。
function Greeting(props: { name: string }) {
return <p>こんにちは、{props.name}さん!</p>;
}
function App() {
return <Greeting name="かつコーチ" />;
}
App から Greeting に name="かつコーチ" という値を渡し、Greeting 側は props.name として受け取っています。
なぜPropsが必要なのか
Reactの大きな強みは、UIを部品(コンポーネント)に分割して再利用できることです。
ただし、部品を使い回すには「同じ見た目のコンポーネントに、違うデータを表示させたい」という場面が必ず出てきます。
たとえばユーザー一覧画面で、名前だけが違うカードを何枚も表示したいケースです。
このとき、コンポーネントの中身を毎回書き換えるのではなく、外から値を注入できれば効率的です。
Propsはまさにこの「外から値を注入する」ための仕組みで、Reactコンポーネントを部品として再利用可能にする土台になっています。
基本の書き方
親から子へ値を渡す
Propsを渡す側は、JSXの属性のようにして値を渡します。
function UserCard(props: { name: string; age: number }) {
return (
<div>
<p>名前:{props.name}</p>
<p>年齢:{props.age}歳</p>
</div>
);
}
function App() {
return <UserCard name="佐藤太郎" age={28} />;
}
文字列は name="佐藤太郎" のようにダブルクォートで、数値や変数を渡すときは age={28} のように波括弧で囲みます。
複数のPropsをまとめて渡す
Propsが増えてくると、1つずつ書くのが面倒になってきます。
そんなときは、オブジェクトをスプレッド構文で展開して渡すこともできます。
const userData = { name: "佐藤太郎", age: 28 };
function App() {
return <UserCard {...userData} />;
}
{...userData} と書くことで、オブジェクトのキーがそのままPropsの名前として渡されます。
TypeScriptで型を付ける
Propsの型は、多くの場合 interface や type で定義してから使います。
interface UserCardProps {
name: string;
age: number;
}
function UserCard({ name, age }: UserCardProps) {
return (
<div>
<p>名前:{name}</p>
<p>年齢:{age}歳</p>
</div>
);
}
props.name と毎回書く代わりに、分割代入で { name, age } と受け取る書き方もよく使われます。
型を明示しておくと、渡し忘れや型違いをエディタが教えてくれるので、実務ではほぼ必須の習慣です。
よくあるつまずきポイント
Propsを直接書き換えてしまう
私が初めてPropsを扱ったとき、受け取った値をコンポーネントの中で直接書き換えようとして、動作がおかしくなったことがあります。
❌ Before:Propsを直接書き換える
interface UserCardProps {
name: string;
}
function UserCard(props: UserCardProps) {
// propsは読み取り専用。直接書き換えるのはNG
props.name = props.name + "さん";
return <p>{props.name}</p>;
}
このコードは一見動いているように見えても、TypeScriptの型エラーになりますし、React全体の設計思想にも反しています。
Propsは親から渡された読み取り専用のデータであり、子コンポーネント側で書き換えることは想定されていません。
✅ After:新しい変数に加工してから使う
interface UserCardProps {
name: string;
}
function UserCard(props: UserCardProps) {
const displayName = `${props.name}さん`;
return <p>{displayName}</p>;
}
受け取った値を書き換えるのではなく、別の変数に加工結果を入れて使うようにすれば、Propsの不変性を保ったまま目的を達成できます。
「Propsは書き換えない」というルールを意識するだけで、この手のバグはかなり減らせます。
応用:一歩先の使い方
children Propsでタグの中身を渡す
children という特別なPropsを使うと、コンポーネントのタグに囲まれた中身をそのまま渡せます。
function Card({ children }: { children: React.ReactNode }) {
return <div className="card">{children}</div>;
}
function App() {
return (
<Card>
<p>ここがchildrenとして渡される内容です</p>
</Card>
);
}
レイアウト用のコンポーネントを作るときによく使われる書き方で、中身を柔軟に差し替えられるのが特徴です。
Propsにデフォルト値を与える
値が渡されなかった場合に備えて、デフォルト値を設定しておくこともできます。
interface ButtonProps {
label?: string;
}
function SubmitButton({ label = "送信" }: ButtonProps) {
return <button>{label}</button>;
}
label?: string と ? を付けて任意項目にし、分割代入時に label = "送信" とデフォルト値を書いておくと、Props未指定でも安全に動作します。
まとめ
この記事のポイント
- Propsは親コンポーネントから子コンポーネントへ値を渡す仕組みで、関数の引数に近いイメージ
- コンポーネントを再利用可能な部品にするために欠かせない仕組み
- Propsは読み取り専用なので、直接書き換えず新しい変数に加工して使う
childrenを使うとタグに囲まれた中身をそのまま渡せる- デフォルト値を設定すれば、Props未指定でも安全に動作する
次に読むべき記事
Propsは「外から渡される値」でしたが、コンポーネント自身が持つ値を扱いたい場面も出てきます。
次回は、そのための仕組みである「useState」の基本を解説します。
→ 次の記事:useStateの基本:状態を持つコンポーネントを作る