Reactで子コンポーネントから親に値を渡す方法

React

こんにちは、白々さじきです。

今回は、Reactで子コンポーネントから親コンポーネントに値を渡す方法についてまとめます。

コンポーネントを分割して実装していたとき、「子でクリックしたときに親のstateを更新したい」という場面で詰まったので、備忘録も兼ねて書きます。(私の学習のために生成AIに作らせた文章を一部加筆修正しました。)

結論

Reactでは子から親に直接データを送れません。代わりに「関数をpropsとして渡す」パターンを使います。親が関数を定義して子に渡し、子がイベント発生時にその関数を呼ぶことで、親のstateを更新できます。

基本的な考え方

Reactのデータの流れは「親 → 子」の一方向が基本です。子から親に直接データを送ることはできません。

代わりに、次のパターンを使います。

  1. 親が「受け取り用の関数」を定義する
  2. その関数を子にpropsとして渡す
  3. 子がイベント発生時に、受け取った関数を呼ぶ(このとき引数でデータを渡す)

関数を呼ぶことで間接的に親の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です。productonSelectany 扱いになり、型チェックの恩恵を受けられません。

// 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タグとして使う

「子から親に渡せない」という制約は最初とまどいますが、「関数を渡す」という発想を知ってからは素直に書けるようになりました。同じところで詰まっている方の参考になれば幸いです。

参考リンク

サポートのお願い

下記リンクからお買い物いただけると、ブログ運営のための費用が増え、有料サービスを利用した記事作成が可能になります。ご協力よろしくお願いします!



コメント

タイトルとURLをコピーしました