props と型のつけ方

Part 2 · コンポーネントと props

props と型のつけ方

部品に情報を渡す。必須と任意の書き分け

前の章で作ったコンポーネントは、いつも同じものしか表示できませんでした。 同じ形で中身だけ違うものを出したいのに、これでは使い物になりません。

そこで、外から値を渡せるようにします。それが props です。

コンポーネントが関数なら、props はその引数です。 新しい概念ではありません。

props は関数の引数

値を渡すときは、HTML の属性と同じ書き方をします。

<Member name="さとう" role="デザイナー" />

受け取る側では、これがひとつのオブジェクトとして届きます。

// 届いているもの
{ name: "さとう", role: "デザイナー" }

オブジェクトで届くので、Part 0 でやった分割代入がそのまま使えます。React のコードがこの形をしているのはそのためです。

// 受け取るところで、直接取り出す
function Member({ name, role }: Props) {
  return <p>{name}</p>;
}
同じ部品に違う値を渡す

関数はひとつ、表示は 2 種類

さとう

デザイナー

すずき

役割は未設定

props-basic.tsx

// 受け取る値の形を決める
type Props = {
  name: string;
  // ? を付けた項目は、渡さなくてもよい
  role?: string;
};

function Member({ name, role }: Props) {
  return (
    <div className="rounded-md border p-3">
      <p className="font-semibold">{name}</p>
      <p className="text-muted-foreground">{role ?? "役割は未設定"}</p>
    </div>
  );
}

export function PropsBasic() {
  return (
    <div className="flex flex-col gap-2.5">
      <Member name="さとう" role="デザイナー" />
      {/* role を渡さなくてもエラーにならない */}
      <Member name="すずき" />
    </div>
  );
}

受け取る値の形を決める

props に型を付けておくと、渡し忘れや打ち間違いをその場で教えてもらえます

type Props = {
  name: string;   // 必ず渡す
  role?: string;  // 渡さなくてもよい
};
  • name を渡し忘れる → エラーになる
  • nane と打ち間違える → エラーになる
  • name={123} と数値を渡す → エラーになる

どれも、動かす前に赤線が出ます。 型を書く手間より、これで助かる時間のほうがずっと大きくなります。

props-basic.tsx

// 受け取る値の形を決める
type Props = {
  name: string;
  // ? を付けた項目は、渡さなくてもよい
  role?: string;
};

function Member({ name, role }: Props) {
  return (
    <div className="rounded-md border p-3">
      <p className="font-semibold">{name}</p>
      <p className="text-muted-foreground">{role ?? "役割は未設定"}</p>
    </div>
  );
}

export function PropsBasic() {
  return (
    <div className="flex flex-col gap-2.5">
      <Member name="さとう" role="デザイナー" />
      {/* role を渡さなくてもエラーにならない */}
      <Member name="すずき" />
    </div>
  );
}

渡されなかったときの値を決めておく

?? を毎回書く代わりに、 受け取るところで初期値を決めておくこともできます。

function Badge({ label, tone = "normal" }: Props) {

tone が渡されなかったときは "normal" になります。これも React の機能ではなく、 JavaScript の分割代入がもともと持っている書き方です。

初期値のある props

tone を渡さないほうは normal になっている

通常注意

受け取る値を絞る

このデモでは、tone の型を "normal" | "warning" にしています。 Part 0 でやった union です。

こうしておくと、tone="warn" のような打ち間違いがエラーになります。渡せる値そのものを制限してしまうのが、 props に型を付ける最大の効果です。

props-default.tsx

type Props = {
  label: string;
  // 決まった値のどれか、しか受け取らないようにする
  tone?: "normal" | "warning";
};

// 受け取るところで = を書くと、渡されなかったときの値になる
function Badge({ label, tone = "normal" }: Props) {
  return (
    <span
      className={
        tone === "warning"
          ? "rounded border border-amber-500 px-2 py-0.5 text-amber-700"
          : "rounded border px-2 py-0.5 text-muted-foreground"
      }
    >
      {label}
    </span>
  );
}

export function PropsDefault() {
  return (
    <div className="flex flex-wrap gap-2.5">
      <Badge label="通常" />
      <Badge label="注意" tone="warning" />
    </div>
  );
}

渡し方のこまごまとした話

// 文字列はそのまま書ける
<Member name="さとう" />

// それ以外は波括弧に入れる
<Member age={20} tags={["a", "b"]} onSave={handleSave} />

// 値が true のときは、名前だけでよい
<Member isAdmin />        // isAdmin={true} と同じ

文字列以外を渡すときに波括弧が要るのは、そこから先が JavaScript だからです。 JSX の章でやった「波括弧の中は JavaScript に戻る」と同じ話です。

props-basic.tsx

// 受け取る値の形を決める
type Props = {
  name: string;
  // ? を付けた項目は、渡さなくてもよい
  role?: string;
};

function Member({ name, role }: Props) {
  return (
    <div className="rounded-md border p-3">
      <p className="font-semibold">{name}</p>
      <p className="text-muted-foreground">{role ?? "役割は未設定"}</p>
    </div>
  );
}

export function PropsBasic() {
  return (
    <div className="flex flex-col gap-2.5">
      <Member name="さとう" role="デザイナー" />
      {/* role を渡さなくてもエラーにならない */}
      <Member name="すずき" />
    </div>
  );
}

理解できたか確かめる

確認クイズ

props はコンポーネントに何として届く?

確認クイズ

type Props = { tone?: 'normal' | 'warning' } と書く利点は?

props-basic.tsx

// 受け取る値の形を決める
type Props = {
  name: string;
  // ? を付けた項目は、渡さなくてもよい
  role?: string;
};

function Member({ name, role }: Props) {
  return (
    <div className="rounded-md border p-3">
      <p className="font-semibold">{name}</p>
      <p className="text-muted-foreground">{role ?? "役割は未設定"}</p>
    </div>
  );
}

export function PropsBasic() {
  return (
    <div className="flex flex-col gap-2.5">
      <Member name="さとう" role="デザイナー" />
      {/* role を渡さなくてもエラーにならない */}
      <Member name="すずき" />
    </div>
  );
}

この章のまとめ

  • props はコンポーネント(関数)の引数
  • いくつ書いても、届くのはひとつのオブジェクト
  • 型を付けると、渡し忘れ・打ち間違い・型違いが動かす前に分かる
  • ? は任意の項目。ないときの表示も用意しておく
  • { tone = "normal" } で初期値を決められる

props-basic.tsx

// 受け取る値の形を決める
type Props = {
  name: string;
  // ? を付けた項目は、渡さなくてもよい
  role?: string;
};

function Member({ name, role }: Props) {
  return (
    <div className="rounded-md border p-3">
      <p className="font-semibold">{name}</p>
      <p className="text-muted-foreground">{role ?? "役割は未設定"}</p>
    </div>
  );
}

export function PropsBasic() {
  return (
    <div className="flex flex-col gap-2.5">
      <Member name="さとう" role="デザイナー" />
      {/* role を渡さなくてもエラーにならない */}
      <Member name="すずき" />
    </div>
  );
}