【React】ボタンクリックで入力フォームを動的に追加する

ボタンクリックなどで動的に入力フォームを追加する方法を実装するたびにやり方を忘れるので備忘録で残しておきます。※React 17+

方法

まず、フォームのデータの型をTypeScriptで定義します

interface Form {
  id: number;
  title: string;
  name: string;
}

動的フォームを作成する際、フォームの中に入ってくるデータのフォーマットは次のようなオブジェクトであることが多いと思います。

[
  { "id": 1, "title": "タイトル1", "name": "太郎" },
  { "id": 2, "title": "タイトル2", "name": "次郎" }
]

Reactでは、このデータをStateとして管理します。

const [forms, setForms] = useState<Form[]>([]);

次に、一意のIDを生成するためのカウンターとしてのstateを追加します。

const [idCounter, setIdCounter] = useState<number>(0);

追加

追加ボタンをクリックした際は、次のように新しいフォームデータをStateに追加します。

const addForm = () => {
  const formBody: Form = {
    id: idCounter,
    title: "",
    name: "",
  };
  setIdCounter(prevId => prevId + 1);
  setForms(prevForms => [...prevForms, formBody]);
};

削除

削除はmap関数を使ってフォームのデータをループし、該当するindexのデータを削除します。

const deleteForm = (id: number) => {
  setForms(prevForms => prevForms.filter(form => form.id !== id));
};

入力ハンドラ

入力ごとの再レンダリングを避けるために、useCallback フックを使用して handleInputChange 関数をメモ化します。これにより、関数の新しいインスタンスが不必要に作成されるのを防ぎ、特定のフォームに対する変更だけを行うようにします。

const handleInputChange = useCallback(
  (id: number, key: keyof Form) => 
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setForms(prevForms => 
        prevForms.map(form => 
          form.id === id ? {...form, [key]: e.target.value} : form
        )
      );
  },
  []  // 依存配列は空です
);

コンポーネントのコード

import React, { useState, useCallback } from 'react';

type Form = {
  id: number;
  title: string;
  name: string;
};

const DynamicForm: React.FC = () => {
  const [forms, setForms] = useState<Form[]>([]);
  const [idCounter, setIdCounter] = useState<number>(1);

  const addForm = () => {
    const formBody: Form = {
      id: idCounter,
      title: "",
      name: "",
    };
    setIdCounter(prevId => prevId + 1);
    setForms(prevForms => [...prevForms, formBody]);
  };

  const deleteForm = (id: number) => {
    setForms(prevForms => prevForms.filter(form => form.id !== id));
  };

const handleInputChange = useCallback(
  (id: number, key: keyof Form) => 
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setForms(prevForms => 
        prevForms.map(form => 
          form.id === id ? {...form, [key]: e.target.value} : form
        )
      );
  },
  []  // 依存配列は空です
);

  return (
    <div>
      <button onClick={addForm}>追加</button>
      {forms.map(form => (
        <div key={form.id}>
          <input
            type="text"
            placeholder="タイトル"
            value={form.title}
            onChange={handleInputChange(form.id, 'title')}
          />
          <input
            type="text"
            placeholder="名前"
            value={form.name}
            onChange={handleInputChange(form.id, 'name')}
          />
          <button onClick={() => deleteForm(form.id)}>削除</button>
        </div>
      ))}
    </div>
  );
}

export default DynamicForm;

このコンポーネントを利用することで、動的に入力フォームを追加・削除する機能がReact、TypeScriptで実現できます。

コメントを残す

入力エリアすべてが必須項目です。メールアドレスが公開されることはありません。

内容をご確認の上、送信してください。