React Hook Formはなぜ使う?useStateでフォームを作った場合と比較する

アイキャッチ画像

はじめに

Reactでフォームを作るとき、最初は useState を使って実装することが多いと思います。

例えば、名前だけを入力するフォームなら、

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

return (
  <input
    value={name}
    onChange={(e) => setName(e.target.value)}
  />
);

これだけで十分です。

しかし、実際のフォームでは、

名前
メールアドレス
パスワード
電話番号
住所

のように項目が増えていきます。

さらに、

必須チェック
文字数チェック
メールアドレス形式
エラー表示
送信処理

まで必要になると、少しずつ管理するコードが増えていきます。

そこで使われることが多いのが React Hook Form です。

この記事では、useState でフォームを作る場合と比較しながら、React Hook Formを使う理由を整理します。


useStateでもフォームは作れる

まず、名前とメールアドレスを入力するフォームを考えます。

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

const handleSubmit = (e: React.FormEvent) => {
  e.preventDefault();

  console.log({
    name,
    email,
  });
};

入力欄は、

<input
  value={name}
  onChange={(e) => setName(e.target.value)}
/>

<input
  value={email}
  onChange={(e) => setEmail(e.target.value)}
/>

のように書けます。

項目が2つ程度なら、特に問題ありません。

ただし、項目が増えると、

const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [phone, setPhone] = useState("");
const [address, setAddress] = useState("");

のようにstateも増えていきます。

つまり、

入力項目を追加
↓
stateを追加
↓
onChangeを追加

という管理が必要になります。


バリデーションが入るとさらに増える

例えば、

名前は必須
メールアドレスも必須

という条件を追加するとします。

入力値だけではなく、エラーも管理する必要があります。

const [nameError, setNameError] = useState("");
const [emailError, setEmailError] = useState("");

送信時には、

if (!name) {
  setNameError("名前を入力してください");
}

if (!email) {
  setEmailError("メールアドレスを入力してください");
}

のような処理も必要になります。

もちろん、この方法が間違っているわけではありません。

ただ、フォームが大きくなるにつれて、

入力値
エラー
バリデーション
onChange
送信処理

を自分で管理する範囲が増えていきます。


React Hook Formを使うとどうなる?

同じようなフォームをReact Hook Formで書くと、次のようになります。

import { useForm } from "react-hook-form";

type FormValues = {
  name: string;
  email: string;
};

function UserForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormValues>();

  const onSubmit = (data: FormValues) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input
        {...register("name", {
          required: "名前を入力してください",
        })}
      />

      {errors.name && (
        <p>{errors.name.message}</p>
      )}

      <input
        {...register("email", {
          required: "メールアドレスを入力してください",
        })}
      />

      {errors.email && (
        <p>{errors.email.message}</p>
      )}

      <button type="submit">
        送信
      </button>
    </form>
  );
}

useState 版のように、

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

と入力項目ごとにstateを用意していません。

代わりに、

register("name")

のように、その入力欄をReact Hook Formへ登録します。


register は何をしているのか

最初にReact Hook Formを触ったとき、

{...register("name")}

が少し分かりにくいと思います。

これは、

この入力欄を
nameというフォーム項目として管理する

ためのものです。

例えば、

<input {...register("name")} />

とすると、送信時に、

const onSubmit = (data) => {
  console.log(data);
};

へ、

{
  name: "田中",
  email: "test@example.com"
}

のようなデータが渡されます。

入力値を項目ごとの useState で管理しなくても、フォームとしてまとめて扱えるようになります。


バリデーションもフォームにまとめられる

例えば、名前を必須にするなら、

<input
  {...register("name", {
    required: "名前を入力してください",
  })}
/>

と書けます。

メールアドレスなら、

<input
  {...register("email", {
    required: "メールアドレスを入力してください",
  })}
/>

のように設定できます。

エラーは、

formState.errors

から取得できます。

{errors.email && (
  <p>{errors.email.message}</p>
)}

つまり、

入力項目
+
バリデーション
+
エラー

をReact Hook Formの仕組みでまとめて管理できます。


useStateとReact Hook Formの違い

簡単に整理すると、次のようになります。

項目useStateReact Hook Form
小さいフォームシンプル少し準備が必要
入力値自分でstate管理フォームとして管理
バリデーション自分で実装registerで設定可能
エラー自分で管理formState.errors
項目が増えた場合管理コードが増えやすい整理しやすい

React Hook Formには、不要な再レンダリングを抑えやすいという特徴もあります。

useState で入力値を管理すると、

入力
↓
setState
↓
state更新
↓
再レンダリング

という流れになります。

React Hook Formは、すべての入力値を useState で管理する必要がないため、項目数の多いフォームでも扱いやすく設計されています。


すべてReact Hook Formにする必要はない

React Hook Formが便利だからといって、

フォームなら全部React Hook Formにするべき

というわけではないと思います。

例えば、

検索ボックス1つだけ

のようなUIなら、useState だけでも十分です。

React Hook Formのメリットが出やすいのは、

入力項目が多い

バリデーションが多い

エラー表示が必要

フォーム全体の状態を管理したい

といったケースです。

つまり、

小さなフォーム
↓
useStateでも十分

複雑なフォーム
↓
React Hook Formが便利

という使い分けで考えると分かりやすいと思います。


まとめ

Reactのフォームは useState だけでも作れます。

しかし、入力項目が増えてくると、

state
onChange
バリデーション
エラー

など、自分で管理するコードも増えていきます。

React Hook Formを使うことで、

register
handleSubmit
formState

を中心にフォームを管理できるようになります。

自分の中では、

useState
↓
入力値を自分で管理する

React Hook Form
↓
フォーム全体の管理を任せる

という違いで考えると理解しやすいです。

React Hook Formは、useStateではフォームを作れないから使うのではありません。

フォームが複雑になったとき、入力値やバリデーション、エラーなどの管理を整理しやすくするために使う。

これがReact Hook Formを使う理由だと思います。