Props
この章では、コンポーネントにデータを渡すためのPropsについて学びます。
この章で学ぶこと
- Propsの基本的な使い方
- 型定義の方法
- オプショナルPropsとデフォルト値
- childrenの使い方
この章では03章で作成したプロジェクトを使用します。npm run devで開発サーバーを起動し、コードを書きながら学習を進めましょう。
Propsとは
Props(プロパティの略)は、親コンポーネントから子コンポーネントにデータを渡すための仕組みです。
Propsの基本
Propsを受け取る
function Greeting(props: { name: string }) {
return <h1>こんにちは、{props.name}さん!</h1>
}
Propsを渡す
function App() {
return <Greeting name="田中" />
}
分割代入
Propsは分割代入で受け取るのが一般的です。※分割代入については02章で解説しています。
function Greeting({ name }: { name: string }) {
return <h1>こんにちは、{name}さん!</h1>
}
なぜ{ name }: stringではダメなのか?
分割代入の型注釈で混乱しやすいポイントです。
// これはエラー
function Greeting({ name }: string) { }
型注釈は「分割代入する前のオブジェクト全体」を表します。
引数として受け取るもの 型注釈
↓ ↓
{ name } : { name: string }
↑ ↑
分割代入で取り出す 元のオブジェクトの型
展開すると、以下と同じ意味になります。
function Greeting(props: { name: string }) {
const { name } = props // propsはオブジェクトだから分割代入できる
return <h1>こんにちは、{name}さん!</h1>
}
{ name } は「オブジェクトから取り出す」構文なので、型もオブジェクト型でなければ矛盾します。
JSXのPropsはどうやって渡されるのか?
JSXで書いた属性は、オブジェクトに変換されて関数に渡されます。
// この呼び出しは...
<Greeting name="田中" />
// Reactが内部でこう変換している
Greeting({ name: "田中" })
つまり、JSXの属性がそのままオブジェクトのキーになります。
<UserCard name="田中" age={25} email="test@example.com" />
// ↓ 以下のオブジェクトとして渡される
// { name: "田中", age: 25, email: "test@example.com" }
型定義の分離
Propsの型は別途定義すると見やすくなります。
type GreetingProps = {
name: string
}
function Greeting({ name }: GreetingProps) {
return <h1>こんにちは、{name}さん!</h1>
}
type と interface のどちらを使うか
Props の型は type と interface のどちらでも定義できますが、本書では type に統一しています。ユニオン型・インターセクション型・マップ型などを後から柔軟に組み合わせられる点で type のほうが応用範囲が広いためです。チームで統一されていればどちらでも構いません。
複数のProps
複数のPropsを渡すことができます。
type UserCardProps = {
name: string
age: number
email: string
}
function UserCard({ name, age, email }: UserCardProps) {
return (
<div>
<h2>{name}</h2>
<p>年齢: {age}歳</p>
<p>メール: {email}</p>
</div>
)
}
function App() {
return (
<UserCard
name="田中太郎"
age={25}
email="tanaka@example.com"
/>
)
}
export default App;
試してみよう: Propsを使ったコンポーネント
nameとageをPropsとして受け取り、「こんにちは、〇〇さん(△△歳)!」と表示するGreetingコンポーネントを作ってみましょう。
ヒント
type GreetingProps = { name: string; age: number }で型を定義- 分割代入で
{ name, age }を受け取る - 数値のPropsは
{}で囲んで渡す(age={25})
回答と解説
import './App.css'
type GreetingProps = {
name: string
age: number
}
function Greeting({ name, age }: GreetingProps) {
return <p>こんにちは、{name}さん({age}歳)!</p>
}
// 使用例
function App() {
return <Greeting name="田中" age={25} />
}
export default App
Propsの型を別途定義することで、コードの可読性が向上します。数値のPropsは{}で囲んで渡します(age={25})。文字列以外の値(数値、真偽値、オブジェクト、配列など)はすべて{}で囲む必要があります。
オプショナルProps
必須でないPropsは?を付けてオプショナルにします。
type ButtonProps = {
label: string
disabled?: boolean
}
function Button({ label, disabled = false }: ButtonProps) {
return (
<button disabled={disabled}>
{label}
</button>
)
}
// 使用例
function App() {
return (
<>
<Button label="送信" />
<Button label="送信" disabled />
</>
)
}
export default App;
デフォルト値
オプショナルPropsにはデフォルト値を設定できます。
type AlertProps = {
message: string
type?: 'info' | 'warning' | 'error'
}
function Alert({ message, type = 'info' }: AlertProps) {
const colors = {
info: 'blue',
warning: 'orange',
error: 'red'
}
return (
<div style={{ color: colors[type] }}>
{message}
</div>
)
}
// 使用例
function App() {
return (
<>
<Alert message="情報メッセージです" />
<Alert message="警告メッセージです" type="warning" />
<Alert message="エラーメッセージです" type="error" />
</>
)
}
export default App;
試してみよう: オプショナルPropsを追加
先ほど作成したGreetingコンポーネントにlanguageプロップを追加してみましょう。デフォルトは日本語で、"en"が渡されたら英語で挨拶するようにします。
ヒント
language?: 'ja' | 'en'でユニオン型のオプショナルPropsを定義- デフォルト値
= 'ja'を分割代入で設定 if文や三項演算子で言語に応じた表示を切り替え
回答と解説
import './App.css'
type GreetingProps = {
name: string
age: number
language?: 'ja' | 'en'
}
function Greeting({ name, age, language = 'ja' }: GreetingProps) {
if (language === 'en') {
return <p>Hello, {name} ({age} years old)!</p>
}
return <p>こんにちは、{name}さん({age}歳)!</p>
}
// 使用例
function App() {
return (
<>
<Greeting name="田中" age={25} />
<Greeting name="Tanaka" age={25} language="en" />
</>
)
}
export default App
?を付けるとオプショナルになり、デフォルト値を設定できます。ユニオン型('ja' | 'en')を使うと、許可される値を制限でき、TypeScriptが型チェックしてくれます。
children Props
コンポーネントタグの間に記述した内容は、childrenとして渡されます。
React.ReactNodeとは?
React.ReactNodeは、Reactがレンダリングできるすべての型を表します。
// 簡略化したイメージ(実際の型定義はもう少し複雑です)
type ReactNode =
| ReactElement // <Component /> や <div> など
| string // "テキスト"
| number // 123
| boolean // true/false(何も表示されない)
| null // 何も表示されない
| undefined // 何も表示されない
| ReactNode[] // 配列
childrenの型には通常React.ReactNodeを使用します。
type CardProps = {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<div className="card">
<h2>{title}</h2>
<div className="card-body">
{children}
</div>
</div>
)
}
function App() {
return (
<Card title="お知らせ">
<p>本日のセールは終了しました。</p>
<p>明日もお待ちしています!</p>
</Card>
)
}
export default App;
試してみよう: childrenを使ったCardコンポーネント
titleとchildrenを受け取るCardコンポーネントを作成してみましょう。カードにはボーダーとパディングを付けて、タイトルと中身を表示します。
ヒント
children: React.ReactNodeを型定義に追加{children}で子要素を表示- インラインスタイルで装飾(
border,padding,borderRadius)
回答と解説
import './App.css'
type CardProps = {
title: string
children: React.ReactNode
}
function Card({ title, children }: CardProps) {
return (
<div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
<h2>{title}</h2>
<div>{children}</div>
</div>
)
}
// 使用例
function App() {
return (
<Card title="お知らせ">
<p>本日は休業日です。</p>
<p>明日からよろしくお願いします。</p>
</Card>
)
}
export default App
childrenを使うと、コンポーネントタグの間に記述した内容を受け取れます。これにより、柔軟で再利用性の高いレイアウトコンポーネントが作れます。HTMLの<div>や<section>のように、中身を自由に変えられるコンポーネントを作る際に重宝します。
オブジェクトのスプレッド
オブジェクトのPropsはスプレッド構文で展開できます。
type UserProps = {
name: string
age: number
email: string
}
function UserProfile(props: UserProps) {
return (
<div>
<p>名前: {props.name}</p>
<p>年齢: {props.age}</p>
<p>メール: {props.email}</p>
</div>
)
}
function App() {
const user = {
name: '田中',
age: 30,
email: 'tanaka@example.com'
}
return <UserProfile {...user} />
}
export default App;
Propsの原則
1. Propsは読み取り専用
Propsは子コンポーネント内で変更してはいけません。
// NG: Propsを変更しようとしている
function Greeting({ name }: { name: string }) {
name = '山田' // 構文上は通ってしまうが、やってはいけない
return <h1>こんにちは、{name}さん!</h1>
}
2. 単方向データフロー
データは常に親から子へ流れます。これにより、データの流れが予測しやすくなります。
関数のProps
関数もPropsとして渡せます。これはイベントハンドラーで頻繁に使用されます。※イベントハンドリングの詳細は08章で解説します。
type ButtonProps = {
label: string
onClick: () => void
}
function Button({ label, onClick }: ButtonProps) {
return <button onClick={onClick}>{label}</button>
}
function App() {
const handleClick = () => {
console.log('クリックされました')
}
return <Button label="クリック" onClick={handleClick} />
}
export default App;
ref も Props として渡せる(React 19+)
React 19 からは、ref を通常の Props と同じように関数コンポーネントで受け取れるようになりました。以前の React(18 以下)では forwardRef で包む必要がありましたが、その手間がなくなっています。
// React 19+: refをそのままpropsから受け取れる
type InputProps = {
label: string
ref?: React.Ref<HTMLInputElement>
}
function LabeledInput({ label, ref }: InputProps) {
return (
<label>
{label}
<input ref={ref} />
</label>
)
}
useRef や ref 属性そのものの使い方は 12章で詳しく解説するため、ここでは「ref は Props の一種として扱える」という点だけ押さえておけば十分です。
まとめ
- Propsは親から子へデータを渡す仕組み
- TypeScriptで型を定義して安全に使用
- オプショナルPropsにはデフォルト値を設定できる
childrenでコンポーネントの中身を渡せる- React 19 からは
refも Props として受け取れる(forwardRef不要) - Propsは読み取り専用、データは単方向
次の章では、コンポーネント内部の状態を管理するStateについて学びます。