概要
Propsは、Reactにおけるコンポーネント間のデータ通信メカニズムである。
親コンポーネントから子コンポーネントへ一方向にデータを渡すことができ、これを 単方向データフロー と呼ぶ。
Propsには、オブジェクト、配列、関数、文字列や数値等のプリミティブ値といった任意のTypeScript値を渡すことができる。
渡されたPropsは読み取り専用 (イミュータブル) であり、子コンポーネントから直接変更することはできない。
主な特徴は以下の通りである。
- 親コンポーネントから子コンポーネントへ一方向にデータが流れる
- 任意のTypeScript / JavaScript値 (オブジェクト、配列、関数、プリミティブ) を渡せる
- Propsは読み取り専用であり、受け取ったコンポーネントが変更することはできない
- 分割代入やデフォルト値の指定により、簡潔に記述できる
- TypeScriptと組み合わせることで、型安全なコンポーネント設計が可能になる
Propsの基本
Propsの受け渡し
親コンポーネントは、JSXの属性としてPropsを子コンポーネントへ渡す。
データは常に親から子へ一方向に流れる。(単方向データフロー)
以下の例では、Profile コンポーネントが Avatar コンポーネントへ person と size を渡している。
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
オブジェクトは二重中括弧 {{}} で、数値は単一の中括弧 {} で渡す琴に注意する。
Propsの受け取り
子コンポーネントは、関数の引数として props オブジェクトを受け取る。
interface AvatarProps {
person: { name: string; imageId: string };
size: number;
}
function Avatar(props: AvatarProps) {
let person = props.person;
let size = props.size;
// person と size を使用する処理
}
この方法では、props.<プロパティ名> の形式でアクセスする必要がある。
より簡潔に記述するためには、#分割代入によるPropsの受け取り 次のセクションで説明する分割代入を使用する。
分割代入によるPropsの受け取り
分割代入の基本
関数の引数で分割代入を使用すると、Propsを直接変数として受け取ることができる。
// AvatarProps型は上記で定義済み
function Avatar({ person, size }: AvatarProps) {
// personとsizeが直接利用可能
}
props.person と記述する代わりに person と直接参照でき、ソースコードが簡潔になる。
残余プロパティ
スプレッド構文 ...rest を使用すると、指定したProps以外の残りのPropsをまとめて受け取ることができる。
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant: string;
}
function Button({ variant, ...rest }: ButtonProps) {
return <button className={variant} {...rest} />;
}
- variant
- 取り出して使用するProp
- ...rest
- variant以外の全てのPropsをオブジェクトとしてまとめたもの
- {...rest} で子要素にそのまま展開して渡すことができる
デフォルト値
分割代入によるデフォルト値
分割代入の際に = を使用して、Propが渡されなかった場合のデフォルト値を指定できる。
interface AvatarProps {
person: { name: string; imageId: string };
size?: number;
}
function Avatar({ person, size = 100 }: AvatarProps) {
// sizeが指定されない場合、または undefined の場合、100がデフォルト値として使用される
}
デフォルト値が適用される条件と適用されない条件を以下に示す。
| 渡される値 | デフォルト値の適用 |
|---|---|
| 指定なし (Propが存在しない) | 適用される |
undefined |
適用される |
null |
適用されない (nullのまま) |
0 |
適用されない (0のまま) |
false |
適用されない (falseのまま) |
children
childrenの基本
JSXタグ内にネストされたコンテンツは、自動的に children Propとして子コンポーネントへ渡される。
interface CardProps {
children: React.ReactNode;
}
function Card({ children }: CardProps) {
return (
<div className="card">
{children}
</div>
);
}
export default function Profile() {
return (
<Card>
<Avatar />
</Card>
);
}
Card コンポーネントは、タグ内にどのような要素が渡されるかを知らなくてもよい。
{children} の位置にネストされたコンテンツが展開される。
childrenの活用パターン
children を活用することにより、汎用的なラッパーコンポーネントを作成できる。
- Cardコンポーネントの例
interface CardProps { children: React.ReactNode; className?: string; } function Card({ children, className }: CardProps) { return ( <div className={`card ${className}`}> {children} </div> ); }
- 複数のセクションを持つLayoutコンポーネントの例
- この場合、sidebar と content のように、Propとして複数のJSX要素を渡す方法も有効である。
interface LayoutProps { sidebar: React.ReactNode; content: React.ReactNode; } function Layout({ sidebar, content }: LayoutProps) { return ( <div className="layout"> <aside>{sidebar}</aside> <main>{content}</main> </div> ); } <Layout sidebar={<Navigation />} content={<MainContent />} />
- Modalコンポーネントの例
isOpenがfalseの場合は、何もレンダリングしない設計にすることで、表示制御を親コンポーネント側で行える。interface ModalProps { isOpen: boolean; title: string; children: React.ReactNode; onClose: () => void; } function Modal({ isOpen, title, children, onClose }: ModalProps) { if (!isOpen) return null; return ( <div className="modal-overlay"> <div className="modal"> <h2>{title}</h2> {children} <button onClick={onClose}>Close</button> </div> </div> ); }
スプレッド構文によるPropsの展開
スプレッド構文を使用すると、Propsをまとめて子コンポーネントへ渡すことができる。
以下の2つの記述は同じ動作をする。
interface ProfileProps {
person: { name: string; imageId: string };
size: number;
isSepia: boolean;
thickBorder: boolean;
}
// 冗長な書き方
function Profile({ person, size, isSepia, thickBorder }: ProfileProps) {
return (
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
);
}
// スプレッド構文 (簡潔な記述)
function Profile(props: ProfileProps) {
return <Avatar {...props} />;
}
スプレッド構文は簡潔である一方、どのPropsが渡されるかが不明確になる場合がある。
そのため、使用は最小限にとどめ、Propsを個別に列挙する方が可読性は高い。
全てのコンポーネントでスプレッド構文を使用することは避けること。
Props型定義 (TypeScript)
interfaceによるProps型の定義
TypeScriptでは、interface を使用してPropsの型を定義することができる。
型定義により、コンパイル時に型の不一致を検出でき、IDEの補完機能も有効になる。
interface MyButtonProps {
title: string;
disabled: boolean;
}
function MyButton({ title, disabled }: MyButtonProps) {
return <button disabled={disabled}>{title}</button>;
}
オプショナルPropsと必須Props
? を付加することでオプショナル (省略可能) なPropsを定義できる。
? のないPropsは必須となり、渡されない場合はコンパイルエラーになる。
interface CardProps {
title: string; // 必須
description?: string; // オプショナル
onClick?: () => void; // オプショナルなコールバック
}
childrenの型定義
children Propの型には React.ReactNode を使用することが推奨される。
interface ModalRendererProps {
title: string;
children: React.ReactNode;
}
children の型の選択肢を以下に示す。
| 型 | 説明 |
|---|---|
| React.ReactNode(推奨) | 文字列、数値、JSX 要素、null、undefined、配列等全ての値に対応する。 最も汎用的であり、通常はこちらを使用する。 |
| React.ReactElement | JSX要素のみを受け付ける、より限定的な型 文字列や数値を受け付けないため、必要な場合にのみ使用する。 |
イベントハンドラの型
Reactは各DOMイベントに対応する型を提供している。
interface InputFieldProps {
value: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
}
代表的なイベントハンドラの型を以下に示す。
| 型 | 対応するイベント |
|---|---|
React.ChangeEvent<HTMLInputElement> |
入力フィールドの値変更 |
React.MouseEvent<HTMLButtonElement> |
ボタンのクリック |
React.FormEvent<HTMLFormElement> |
フォームの送信 |
React.KeyboardEvent<HTMLInputElement> |
キーボード入力 |
React.FocusEvent<HTMLInputElement> |
フォーカスの変更 |
Propsの設計原則
単方向データフロー
Reactでは、データは親コンポーネントから子コンポーネントへ一方向に流れる。
子コンポーネントが親の状態を直接変更することはできない。
子から親へデータを伝える場合は、コールバック関数をPropsとして渡す方法を使用する。
以下の例では、setFilterText 関数を onFilterTextChange Propとして渡すことで、子コンポーネントが親の状態を更新できる。
interface SearchBarProps {
filterText: string;
onFilterTextChange: (text: string) => void;
}
function Parent() {
const [filterText, setFilterText] = useState<string>('');
return (
<SearchBar
filterText={filterText}
onFilterTextChange={setFilterText}
/>
);
}
function SearchBar({ filterText, onFilterTextChange }: SearchBarProps) {
return (
<input
value={filterText}
onChange={(e) => onFilterTextChange(e.target.value)}
/>
);
}
この設計パターンにより、状態の変更箇所が明確になり、データの流れを追いやすくなる。
Propsの設計における主な原則を以下に示す。
| 項目 | 説明 |
|---|---|
| データは親から子へ一方向に渡す | 子コンポーネントは受け取ったPropsを直接変更しない。 |
| 子から親への通信はコールバック関数で行う | onChange、onClose、onSubmit 等の命名規則を使用する。
|
| Propsは最小限にする | 不要なPropsを渡さないことで、コンポーネントの責務を明確にする。 |
| TypeScriptで型を定義する | 型定義により、意図しない使用を防ぎ、ソースコードの可読性を高める。 |
関連情報