React Hook Form 基礎編:useStateでのフォーム管理を卒業しよう
2026-02-17
azblob://2026/02/17/eyecatch/2026-02-17-react-hook-form-basic-000.png

React Hook Formを使ってきて、慣れてきたので共有です!

目次

  • 1. はじめに:useStateでフォーム管理って大変...
  • 2. useStateでフォームを作ってみる
    • 2-1. シンプルなフォーム例(name / email)
    • 2-2. 項目が増えると何が起きる?
    • コラム①:Reactとstateについて
    • コラム②:制御コンポーネントと非制御コンポーネント
  • 3. React Hook Formで同じフォームを書き換える
    • 3-1. インストール
    • 3-2. 最小構成(register / handleSubmit)
  • 4. 何が嬉しいのか(useStateと比べてラクになる点)
  • 5. まとめ

1. はじめに:useStateでフォーム管理って大変...

Reactでフォームを作るとき、まずはuseStateで管理することが多いと思います。 
ただ、入力項目が増えてくると「stateが増える」「onChangeが増える」「バリデーションが散らかる」など、だんだん辛くなってきます。

今回は、フォームの入力値を管理し、バリデーションも簡単に行えるReact Hook Formを紹介したいと思います。

2. useStateでフォームを作ってみる

コラム①:Reactとstateについて

stateは「コンポーネントが覚えている値」で、stateが更新されると、Reactはコンポーネントを再レンダリング。 
フォームをuseStateで管理する場合、入力のたびにstate更新 → 再レンダリングが起きます。

コラム②:制御コンポーネントと非制御コンポーネント

制御コンポーネント(Controlled):valueをstateで(React側で)管理。 
非制御コンポーネント(Uncontrolled):値をDOM側で保持し、必要なときにref属性で参照する。


React Hook Formは基本的に非制御として動作。制御コンポーネントとして値を管理することも可能。

2-1. シンプルなフォーム例(name / email)

まずはuseStateを使ったフォームの例です。

名前とメールアドレスを入力するだけのシンプルなものですが、stateを定義していたり、イベントを受け取る関数があったり、inputタグにも色々な属性があったりとシンプルな例でも「ごちゃごちゃ」感がありますよね。
実務では他のロジックのstateもあるので、とても見にくくなります。さらに、GitHub Copilotのようなコード生成AIを使えば、フォーム項目を簡単に追加できてしまいます。その手軽さゆえに、気づけばstateとonChangeハンドラのペアが大量に生成されてしまいます。

TypeScriptimport React, { useState } from "react";
export const SampleFormUseState = () => {
  // 管理する値を定義
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    // それぞれの状態をコンソールに出力(実際はAPI送信など)
    console.log({ name, email });
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name
        <input
          value={name}
          onChange={(e) => setName(e.target.value)}
          placeholder="Taro"
        />
      </label>
      <label>
        Email
        <input
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          placeholder="taro@example.com"
        />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};

2-2. 項目が増えると何が起きる?

  • 入力項目ごとにstateが増える
  • onChangeが増える(ほぼ同じ処理何度も書く)
    • 「ほぼ」というのが共通化しにくくて厄介
  • エラー表示やバリデーションを追加すると、さらにstateやロジックが増える
    • フォームのstate
    • バリデーションエラーのstate
    • その他のstate

3. React Hook Formで同じフォームを書き換える

3-1. インストール

まずはインストール!

npm install react-hook-form

3-2. 最小構成(register / handleSubmit)

こちらも先ほどの例をReact Hook Formで書き換えたものです。シンプル!綺麗!

register: input などの入力要素をReact Hook Formに登録するための関数。これにより、入力値の追跡やバリデーション(必須入力、文字数制限など)が簡単になる。

TypeScript// 例:必須チェック
<input {...register("name") , { required: true }} placeholder="Taro" />

handleSubmit: フォームの送信イベント(onSubmit)でこの関数を使うことで、送信前に自動でバリデーションが実行される。バリデーションが成功した場合にのみ、引数に渡した関数(例では onSubmit)が、フォームのデータ(data)を引数に受け取って実行される。
以下のように第二引数にも関数を渡すことができ、バリデーションエラーが発生した際に実行される。

TypeScript// onError: エラーハンドリング関数
<form onSubmit={handleSubmit(onSubmit, onError)}>

TypeScript
import React from "react";
import { useForm } from "react-hook-form";
// フォームで扱う型を定義
type FormData = {
  name: string;
  email: string;
};

export const SampleFormRHF = () => {
  const { register, handleSubmit } = useForm<FormData>();
  
  const onSubmit = (data: FormData) => {
    console.log(data);
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <label>
        Name
        <input {...register("name")} placeholder="Taro" />
      </label>
      <label>
        Email
        <input {...register("email")} placeholder="taro@example.com" />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
};

4. 何が嬉しいのか

  • 入力項目ごとにuseStateを書かなくてよい
  • onChangeハンドラを大量に書かなくてよい
  • フォームが大きくなっても管理が破綻しにくい
    • 型と入力コンポーネントを追加するだけで良い
  • 非制御ベースなので、不要な再レンダリングを抑えやすい

5. まとめ

小さいフォームならuseStateでも十分ですが、項目が増えると途端に管理が大変になります。 
React Hook Formは「フォームはフォーム専用ライブラリに任せる」という選択肢を提供してくれるので、実務でもかなり助かります。