こんにちは、白々さじきです。
今回は、Reactで子コンポーネントから親コンポーネントに値を渡す方法についてまとめます。
コンポーネントを分割して実装していたとき、「子でクリックしたときに親のstateを更新したい」という場面で詰まったので、備忘録も兼ねて書きます。(私の学習のために生成AIに作らせた文章を一部加筆修正しました。)
結論
Reactでは子から親に直接データを送れません。代わりに「関数をpropsとして渡す」パターンを使います。親が関数を定義して子に渡し、子がイベント発生時にその関数を呼ぶことで、親のstateを更新できます。
基本的な考え方
Reactのデータの流れは「親 → 子」の一方向が基本です。子から親に直接データを送ることはできません。
代わりに、次のパターンを使います。
- 親が「受け取り用の関数」を定義する
- その関数を子にpropsとして渡す
- 子がイベント発生時に、受け取った関数を呼ぶ(このとき引数でデータを渡す)
関数を呼ぶことで間接的に親のstateを更新できます。
コード例
商品リストで「選択中の商品」を親が管理する例で見てみます。
親コンポーネント
type Product = {
id: string;
name: string;
price: number;
};
const ProductList: FC = () => {
const [selectedId, setSelectedId] = useState<string | null>(null);
const products: Product[] = [
{ id: 'a', name: 'りんご', price: 100 },
{ id: 'b', name: 'バナナ', price: 80 },
{ id: 'c', name: 'みかん', price: 60 },
];
// ① 受け取り用の関数を定義する
const handleSelect = (id: string) => {
setSelectedId(id);
};
const selected = products.find((p) => p.id === selectedId);
return (
<div>
<p>選択中: {selected ? selected.name : 'なし'}</p>
{products.map((product) => (
// ② 子にpropsとして渡す
<ProductItem
key={product.id}
product={product}
onSelect={handleSelect}
/>
))}
</div>
);
};
子コンポーネント
type Props = {
product: Product;
// 「関数」の型: (引数の型) => 戻り値の型
onSelect: (id: string) => void;
};
const ProductItem = ({ product, onSelect }: Props) => {
return (
<button
onClick={() => {
// ③ クリックされたら、受け取った関数を呼ぶ(引数でデータを渡す)
onSelect(product.id);
}}
>
{product.name} — {product.price}円
</button>
);
};
ボタンをクリックすると onSelect(product.id) が呼ばれ、親の setSelectedId が実行されます。親のstateが変わり、「選択中」の表示が更新されます。
関数の型定義の書き方
TypeScriptで「関数を受け取るprops」の型は次のように書きます。
// 基本形
propName: (引数名: 引数の型) => 戻り値の型;
// 何も返さない関数は void
onSelect: (id: string) => void;
// 引数が複数の場合
onChange: (id: string, value: number) => void;
void は「何も返さない」という意味です。イベントハンドラは基本的に void を使います。
コンポーネントの型定義: FC<Props> vs Props を直接書く
propsの型をコンポーネントに付ける方法は2通りありますが、どちらでも動作は同じです。
// 書き方①: FC<Props>
const ProductItem: FC<Props> = ({ product, onSelect }) => { ... };
// 書き方②: 引数に直接型注釈(現代的な書き方)
const ProductItem = ({ product, onSelect }: Props) => { ... };
型注釈なしはNGです。product や onSelect が any 扱いになり、型チェックの恩恵を受けられません。
// NG: 型がないと any 扱いになる
const ProductItem = ({ product, onSelect }) => { ... };
コンポーネントはJSXタグとして呼ぶ
コンポーネントを使うときは必ずJSXタグとして書きます。関数として直接呼ぶとReactが状態管理を正しく行えません。
// NG: 関数として直接呼んでいる
products.map((product) =>
ProductItem({ product, onSelect: handleSelect })
);
// OK: JSXタグとして書く
products.map((product) => (
<ProductItem key={product.id} product={product} onSelect={handleSelect} />
));
まとめ
- Reactのデータの流れは「親 → 子」の一方向
- 子から親にデータを渡したいときは「関数をpropsとして渡す」パターンを使う
- 関数の型は
(引数の型) => voidの形で書く - コンポーネントは必ずJSXタグとして使う
「子から親に渡せない」という制約は最初とまどいますが、「関数を渡す」という発想を知ってからは素直に書けるようになりました。同じところで詰まっている方の参考になれば幸いです。
参考リンク
サポートのお願い
下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!

コメント