Tips

ReactのuseStateでオブジェクトを使った複数管理する方法

  • このエントリーをはてなブックマークに追加

「Reactでフォームを作るときに入力欄が複数あるときどうすれば良いのか?」

という自分の経験からReactのuseStateのちょっとしたテクニックをご紹介します。

また動画もあるので必要に応じて使ってください。

Reactでフォームを作るときに入力欄が複数あるとき

例えば以下のようなコーディングがあった場合の話をしていきます。

import React from "react";

const ManyInput = () => {

  return (
    <div>
      <form>
        <div>
          <input
            name="name"
            onChange={handleUser}
            type="text"
            placeholder="お名前"
          />
        </div>
        <div>
          <input
            name="age"
            onChange={handleUser}
            type="text"
            placeholder="年齢"
          />
        </div>
        <div>
          <input
            name="post"
            onChange={handleUser}
            type="text"
            placeholder="郵便番号"
          />
        </div>
        <div>
          <input
            name="address"
            onChange={handleUser}
            type="text"
            placeholder="住所"
          />
        </div>
        <div>
          <input
            name="tel"
            onChange={handleUser}
            type="text"
            placeholder="電話番号"
          />
        </div>
        <div>
          <input
            name="email"
            onChange={handleUser}
            type="text"
            placeholder="メールアドレス"
          />
        </div>
      </form>
    </div>
  );
};

export default ManyInput;

inputタグを使って6種類の入力欄を作っています。

こんな感じで会員登録の画面などで、大量の入力欄を作る場面があります。

初学者向けの学習教材やYoutubeではuseStateを使った管理のやり方を紹介されているケースが多く、以下のような書き方がパッと思いつくことでしょう。

const [name, setName] = useState("");

useStateの詳細な解説は割愛しますが、名前を入力するinputタグに対して入力された値を変数nameとして、nameの中身を更新するのがsetNameとなるわけです。

とても基本的な方法なので初学者の方はこちらからマスターすると良いでしょう。

ただ先ほどのコーディングに対応させようとすると以下のような書き方になります。

import React, { useState } from "react";

const ManyInput = () => {
  const [name, setName] = useState("");
  const [age, setAge] = useState("");
  const [post, setPost] = useState("");
  const [address, setAddress] = useState("");
  const [tel, setTel] = useState("");
  const [email, setEmail] = useState("");

  const handleName = (e) => {
    setName(e.target.value);
  };
  const handleAge = (e) => {
   setAge(e.target.value);
  };
  const handlePost = (e) => {
    setPost(e.target.value);
  };
  const handleAddress = (e) => {
    setAddress(e.target.value);
  };
  const handleTel = (e) => {
    setTel(e.target.value);
  };
  const handleEmail = (e) => {
    setEmail(e.target.value);
  };

  return (
    <div>
      <form>
        <div>
          <input
            value={name}
            onChange={handleName}
            type="text"
            placeholder="お名前"
          />
        </div>
        <div>
          <input
            name={age}
            onChange={handleAge}
            type="text"
            placeholder="年齢"
          />
        </div>
        <div>
          <input
            value={post}
            onChange={handlePost}
            type="text"
            placeholder="郵便番号"
          />
        </div>
        <div>
          <input
            value={address}
            onChange={handleAddress}
            type="text"
            placeholder="住所"
          />
        </div>
        <div>
          <input
            value={tel}
            onChange={handleTel}
            type="text"
            placeholder="電話番号"
          />
        </div>
        <div>
          <input
            value={email}
            onChange={handleEmail}
            type="text"
            placeholder="メールアドレス"
          />
        </div>
      </form>
    </div>
  );
};

export default ManyInput;

全ての入力欄に対して個別にuseStateで値を管理して、inputタグのvalue属性にuseStateで管理している変数を適用するためのonChange関数を個別で定義しています。

先に言っておくと上記のコードで正しく動作するので何も問題ありません。

とはいえ少し冗長ですし、入力欄がどんどん増えるとコードもひたすら長くなっていくことが予想されます。

ReactのuseStateでフォームを作るときに少し楽をする方法

結論、入力欄が複数あるときには以下のような書き方が出来ます。

import React, { useState } from "react";

const ManyInput = () => {
  const [user, setUser] = useState({
    name: "",
    age: "",
    post: "",
    address: "",
    tel: "",
    email: "",
  });

  const handleUser = (e) => {
    setUser((prev) => ({ ...prev, [e.target.name]: e.target.value }));
  };

  return (
    <div>
      <form>
        <div>
          <input
            name="name"
            onChange={handleUser}
            type="text"
            placeholder="お名前"
          />
        </div>
        <div>
          <input
            name="age"
            onChange={handleUser}
            type="text"
            placeholder="年齢"
          />
        </div>
        <div>
          <input
            name="post"
            onChange={handleUser}
            type="text"
            placeholder="郵便番号"
          />
        </div>
        <div>
          <input
            name="address"
            onChange={handleUser}
            type="text"
            placeholder="住所"
          />
        </div>
        <div>
          <input
            name="tel"
            onChange={handleUser}
            type="text"
            placeholder="電話番号"
          />
        </div>
        <div>
          <input
            name="email"
            onChange={handleUser}
            type="text"
            placeholder="メールアドレス"
          />
        </div>
      </form>
    </div>
  );
};

export default ManyInput;

一番の特徴は入力欄を個別にuseStateで管理せずに、「user」という形で一括で管理しています。

useStateの初期値にはオブジェクトも使用できるので、オブジェクトのプロパティとしてそれぞれの入力欄を管理しているのです。

「どのinputタグの値になるかはどう判別するんだ?」となった方もいるかもしれません。

inputタグにはname属性があり、name属性の値と突き合わせしておきます。

~省略~

// 1と2の名前を揃えておく
const [user, setUser] = useState({
  name: "",
  age: "", // 1.ココの名前
  post: "",
  address: "",
  tel: "",
  email: "",
});

~省略~

<div>
   <input
      name="age" // 2.ココの名前
      onChange={handleUser}
      type="text"
      placeholder="年齢"
   />
</div>

~省略~

inputタグのname属性は「[e.target.name]」、入力された値は「e.target.value」とすることでそれぞれ値が取得できます。

それらを利用してinputタグに対するonChange関数は以下のように書くことができます。

~省略~

const [user, setUser] = useState({
  name: "",
  age: "",
  post: "",
  address: "",
  tel: "",
  email: "",
});

~省略~

const handleUser = (e) => {
  setUser((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};

~省略~

全ての入力欄をオブジェクトを初期値としてuseStateで管理していました。

そのためオブジェクトの各プロパティに対応する値を更新すれば良いことになります。

// 最終的にはこのようになる
const [user, setUser] = useState({
  name: "山田太郎",
  age: "20",
  post: "123-4567",
  address: "東京都千代田区",
  tel: "090-111-1111",
  email: "test@test.jp",
});

オブジェクトの各プロパティの値を更新するためにはスプレッド構文を使うことで簡単に実現できます。

スプレッド構文について初見の方がいれば、「オブジェクトの中身を更新するための文法」とでも認識してもらえれば良いでしょう。

const handleUser = (e) => {
  //スプレッド構文でオブジェクトの中身を上書きする
  setUser((prev) => ({ ...prev, [e.target.name]: e.target.value }));
};

「…prev」で「更新前のオブジェクト」という意味になります。

「 [e.target.name] : e.taget.value」で「プロパティ名:値」という意味になっていて、それぞれのプロパティに値をセットしていっています。

更新した値をpropsなど別で使いたい時は、JavaScriptのオブジェクトの文法と同じで「name」のように指定することができます。

const handleUser = (e) => {
  //オブジェクトのなかでnameのみを上書きする
  setUser((prev) => ({ ...prev, name: e.target.value }));
};

上記のような書き方でnameのみ更新したとしても他のプロパティの値はリセットされません。

「…prev」で更新前のオブジェクトを一度コピーしてreturnすることになるためです。

このような書き方を関数型のステート更新とも言ったりして、オブジェクトの中身が何回も変わる可能性があるケースで使います。

ちなみにですがフォームの管理をuseStateで行うことで「レンダリング問題」というものがあり、個々の状況によってはuseStateをそもそも使わない方が良い場合もあります。

その場合はuseRefを使うことになるのですが、詳細は別の記事で解説しているので興味のある方はご覧ください。

また今回参考にした本は以下になりますのでよければどうぞ。

今回参考にした本はこちら

  • このエントリーをはてなブックマークに追加