logo 公式Webサイト

パワフルな
ブログ

動的に増やしたinputタグの入力内容を出力【Next.js,TypeScript,React】

2024年4月22日

ボタンクリックで増やしたinputタグの入力値を、別ボタンクリック時にコンソールに出力させる処理をご紹介します。
今回はコンソールへの出力ですが、API通信を用いたデータ送信にも役立ちます。

全体のコード

import { useState } from 'react';

type Item = {
  id: number;
  textValue: string;
  test3Value:string;
  numberValue: number;
};

const App = () => {
  const [items, setItems] = useState<Item[]>([]);

  const addItem = () => {
    const newItem: Item = {
      id: items.length + 1,
      textValue: '',
      test3Value: '',
      numberValue: 0,
    };

    setItems([...items, newItem]);
  };

  const handleInputChange = (id: number, value: string | number, type: 'text' | 'number' | 'test3') => {
    const newItems = items.map((item) => {
      if (item.id === id) {
        if (type === 'text') {
          return { ...item, textValue: value as string };
        } else if (type === 'number') {
          return { ...item, numberValue: value as number };
        } else if (type === 'test3') {
          return { ...item, test3Value: value as string };
        }
      }
      return item;
    });

    setItems(newItems);
  };

  const confirmItems = () => {
    console.log("Items:", items);
    items.forEach(item => {
      console.log(`Item ${item.id}: Text=${item.textValue}, Number=${item.numberValue} , test3Value=${item.test3Value}`);
    });
  };

  return (
    <div>
      {items.map((item) => (
        <div key={item.id}>
          <input
            type="text"
            id={`item-text-${item.id}`}
            value={item.textValue}
            onChange={(e) => handleInputChange(item.id, e.target.value, 'text')}
          />
          <input
            type="number"
            id={`item-number-${item.id}`}
            value={item.numberValue}
            onChange={(e) => handleInputChange(item.id, parseInt(e.target.value, 10) || 0, 'number')}
          />
          <input
            type="test3"
            id={`item-text-${item.id}`}
            value={item.test3Value}
            onChange={(e) => handleInputChange(item.id, e.target.value, 'test3')}
          />
        </div>
      ))}
      <button onClick={addItem}>アイテム追加</button>
      <button onClick={confirmItems}>アイテム確定</button>
    </div>
  );
};

export default App;

処理の流れ

①使用するオブジェクトの型を定義
②アイテムを格納するステートを用意
③アイテムを追加する関数を作成
④増やしたフォーム要素の状態を管理するための関数を追加
⑤入力アイテムの状態をコンソールに出力する関数

①使用するオブジェクトの型を定義

今回データとして使用するオブジェクトの型を定義します。

type Item = {
  id: number;
  textValue: string;
  test3Value:string;
  numberValue: number;
};

②アイテムを格納するステートを用意

①で定義したオブジェクトが格納されるためのステートを配列で用意します。
この配列の中に各種アイテムのデータが定義した①の型を元に格納される形となります。

const [items, setItems] = useState<Item[]>([]);

③アイテムを追加する関数を作成

「アイテム追加」ボタンを押下した際に初期値の新しいアイテムをitemsの中に格納する関数を作成します。

①で定義したitem型を用いて、各種プロパティの初期値を設定し、itemsの中に新しいオブジェクトとして格納します。

  const addItem = () => {
    const newItem: Item = {
      id: items.length + 1,
      textValue: '',
      test3Value: '',
      numberValue: 0,
    };

    setItems([...items, newItem]);
  };

④増やしたフォーム要素の状態を管理するための関数を追加

各種inputタグの値が更新(onChange)された際に、引数としてid(番号),value(入力値),type(リテラル型)を取得し、

newItems配列に、現状のitemsに格納されているitemに対し、mapメソッドを用いてどのリテラル型に該当するかを仕分け、該当した型のvalue(入力値)を各種用意した初期値の入力値(type=“text”ならtextValueプロパティ)を更新し、変更を加えた配列を再構築します。

  const handleInputChange = (id: number, value: string | number, type: 'text' | 'number' | 'test3') => {
    const newItems = items.map((item) => {
      if (item.id === id) {
        if (type === 'text') {
          return { ...item, textValue: value as string };
        } else if (type === 'number') {
          return { ...item, numberValue: value as number };
        } else if (type === 'test3') {
          return { ...item, test3Value: value as string };
        }
      }
      return item;
    });

    setItems(newItems);
  };

⑤入力アイテムの状態をコンソールに出力する関数

アイテム確定ボタン押下時に、items(配列)と、各種配列の中身をプレフィックスをつけてコンソールに出力します。

  const confirmItems = () => {
    console.log("Items:", items);
    items.forEach(item => {
      console.log(`Item ${item.id}: Text=${item.textValue}, Number=${item.numberValue} , test3Value=${item.test3Value}`);
    });
  };

ここまでが、returnより上に記述する処理の内容となります。
続いてtsx箇所について触れていきます。

TSX箇所説明

・items配列(itemオブジェクトが格納されている配列)に対し、mapメソッドで出力を行い、フォームを出力します。
・包括要素に対してkey設定をします。
・該当要素のidにはidを、typeにはリテラル型を、valueにはitemの初期設定で設定したvalue値を、onChangeではhandleInputChange関数にid,入力値,リテラル型を取る形で記述します。
・type: numberのinputタグについては整数とするためparseIntメソッドを用いています。
・アイテム追加とアイテム確定ボタンを追加し、onClickに各種必要な挙動の関数を記述することで、動作します。

return (
  <div>
    {items.map((item) => (
      <div key={item.id}>
        <input
          type="text"
          id={`item-text-${item.id}`}
          value={item.textValue}
          onChange={(e) => handleInputChange(item.id, e.target.value, 'text')}
        />
        <input
          type="number"
          id={`item-number-${item.id}`}
          value={item.numberValue}
          onChange={(e) => handleInputChange(item.id, parseInt(e.target.value, 10) || 0, 'number')}
        />
        <input
          type="test3"
          id={`item-test3-${item.id}`}
          value={item.test3Value}
          onChange={(e) => handleInputChange(item.id, e.target.value, 'test3')}
        />
      </div>
    ))}
    <button onClick={addItem}>アイテム追加</button>
    <button onClick={confirmItems}>アイテム確定</button>
  </div>
);

以上で、inputタグの増やし方、それをコンソールに出力させる機能の実装ができました。
この機能は動的に増えるinputタグを実装する際に役立つかなと思います。