2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

react-hook-form の使い方とメリットまとめ

Last updated at Posted at 2025-11-27

react-hook-formのメリット

1. 高パフォーマンス(最小限のリレンダリング)

  • react-hook-form は 「非制御コンポーネント (uncontrolled) ベース」 の設計で、
    入力値を React の state に都度詰めず、ref 経由で管理します。
  • その結果、再レンダリング回数が少なく、大きなフォームでも軽いのが特徴です。
  • 公式サイトでも「Minimizes the number of re-renders, minimizes validate computation」と明記されており、
    パフォーマンス重視のフォーム向けに設計されていることが分かります。

2. コードがシンプルで、ボイラープレートが少ない

  • useFormregister, handleSubmit など、API がシンプルで覚えやすい
  • 各 input に value / onChange を自前で書かず、{...register("email")} の一行で
    「値の管理+バリデーション+submit 時の値取得」まで面倒を見てくれます。
  • そのため、フォームの行数がかなり減り、可読性も維持しやすいです。

フォームが 10 項目を超えてくると、
「全部 useState で持つ or 大きなオブジェクトで管理する」よりも、
react-hook-form のほうが圧倒的に見通しが良くなります。

3. バリデーション設計がしやすく、拡張もしやすい

  • resolver という仕組みで、Yup・Zod・Joi などのスキーマバリデーションライブラリと簡単に連携できる

  • バリデーションルールを「スキーマ」としてまとめられるので、

    • どんなルールがあるかが一望できる
    • 再利用もしやすい
    • バックエンドと共通化した設計も取りやすい
  • 単純な必須チェックから、複数項目をまたいだ条件付きバリデーションまで、柔軟に書けます。

4. 大規模・複雑なフォームにも対応しやすい

  • FormProvider + useFormContext を使うことで、
    ネストしたコンポーネント間でフォーム状態を共有しやすい設計になっています。
  • フィールド配列(行の追加・削除があるような明細フォームなど)も
    useFieldArray で扱えるため、動的フォームとの相性も良いです。
  • これらを活かすと、「行追加できる明細」「条件によって表示項目が変わるフォーム」など
    実務でよくあるパターンを無理なく実装できます。

5. UI ライブラリとの相性が良い

  • MUI / Chakra UI / Mantine といった UI ライブラリの input コンポーネントとも組み合わせやすく、
    Controller を使えば「React Hook Form + カスタムコンポーネント」を自然に接続できます。
  • すでに UI コンポーネントライブラリを使っているプロジェクトでも、
    デザインを崩さずにフォームだけ react-hook-form に移行しやすいです。

react-hook-form の基本

1. useForm でフォームを初期化する

まずはコンポーネントの先頭で useForm を呼び出します。

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

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

export const LoginForm = () => {
  const {
    register,       // input とフォームロジックを紐づける
    handleSubmit,   // 送信時に呼ぶ
    formState: { errors }, // バリデーション結果など
  } = useForm<FormValues>();

  // 後述
};
  • useForm<FormValues>()<FormValues>フォームの型(TypeScript 用)

  • ここで返ってくるオブジェクトから、フォーム操作に使う関数を取り出します

    • register
    • handleSubmit
    • formState.errors など

2. register を input に渡す

register を各フィールドに「展開」するのが、react-hook-form の基本スタイルです。

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    <div>
      <label>メールアドレス</label>
      <input
        type="email"
        {...register("email", {
          required: "メールアドレスは必須です",
        })}
      />
      {errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}
    </div>

    <div>
      <label>パスワード</label>
      <input
        type="password"
        {...register("password", {
          required: "パスワードは必須です",
          minLength: {
            value: 8,
            message: "8文字以上で入力してください",
          },
        })}
      />
      {errors.password && (
        <p style={{ color: "red" }}>{errors.password.message}</p>
      )}
    </div>

    <button type="submit">ログイン</button>
  </form>
);

何をしているか?

  • register("email", {...})
    → 「この input は email という名前のフィールドです」と react-hook-form に登録

  • 第 2 引数のオブジェクトで、バリデーションルール も一緒に指定できる

    • required: "必須メッセージ"
    • minLength, maxLength, pattern, validate なども書ける
  • エラーは formState.errors に入るので、errors.email?.message で表示

ポイント

  • valueonChange を自分で書かなくていい

  • ...register() を展開するだけで

    • 値の登録
    • バリデーション設定
    • 送信時の値取得
      を全部やってくれる

3. handleSubmit で送信処理とバリデーションをまとめる

フォームの <form> タグには、onSubmit={handleSubmit(onSubmit)} を指定します。

const onSubmit = (data: FormValues) => {
  // バリデーション OK のときだけ呼ばれる
  console.log(data);
  // data.email / data.password が入っている
};

return (
  <form onSubmit={handleSubmit(onSubmit)}>
    {/* 入力フィールドたち */}
  </form>
);

handleSubmit(onSubmit) の役割:

  1. 全フィールドのバリデーションを実行
  2. エラーがあれば errors にセット → onSubmit は呼ばれない
  3. 問題なければ、フォームの値を引数 data として onSubmit に渡す

なので、onSubmit の中では「もうバリデーション済み」という前提で
API 呼び出しなどの処理を書いて OK です。


react-hook-formで出来ないこと

1. ビジネスロジックやドメインルールまでは面倒を見てくれない

  • 「18歳未満ならこの項目は必須」
  • 「このプランを選んだときだけ上限が変わる」
  • 「社内の申請フローに合わせた複雑なチェック」

こういう業務・ドメイン寄りのルールは、
react-hook-form が自動で判断してくれるわけではなく、結局は自分でロジックを書く必要があります。

2. バックエンドとのスキーマ同期はしてくれない

  • DB のスキーマ
  • API のリクエスト/レスポンス定義
  • OpenAPI/Swagger など

これらと 自動で連携してバリデーションルールを生成する機能はありません。

  • Zod や Yup と組み合わせれば「フロント内の」スキーマ共通化はできる
  • でも「バックエンドからスキーマを引っ張ってきて自動生成」まではしない

ので、バックエンドと完璧に一元管理したいなら別の仕組み(codegen など)が必要です。

3. マルチステップフォーム / 画面またぎの状態管理はやってくれない

react-hook-form は「1 つのフォーム」の中身を扱うのが主な役割です。

  • ステップ1 → ステップ2 → 確認画面 → 完了画面
  • 画面 A で入力して、画面 B で続き、画面 C で確認

みたいな「ページをまたぐウィザード形式」は、

  • react-hook-form で各ページのフォームを持つ
  • その上で、親コンポーネントやグローバル state で全体を保持する

といったラッピング設計が必要で、
「マルチステップフォームをワンライナーで作る」ような機能はありません。

4. 複雑すぎる動的フォームの設計までは助けてくれない

useFieldArray

  • 行の追加/削除がある明細フォーム
  • ネストしたリスト

などは扱えますが、

  • ネストが深くなりすぎる
  • 条件分岐が多すぎる
  • フィールド名が動的に決まる

といった設計そのものの複雑さは、
react-hook-form を入れても勝手に解決されません。

例:

  • 「1ページに 50 項目 × 10 行」
  • 「ロールによって表示項目が変わる」
    → ライブラリが悪いというより、「フォーム設計そのものがカオス」なケースも多い

他ライブラリと組み合わせたときの可能性

1. バリデーションライブラリとの連携で「スキーマ駆動フォーム」に

@hookform/resolvers を使うと、Yup / Zod / Joi / Vest など、好きなバリデーションライブラリをそのまま組み込めます。

  • バリデーションルールをコード上の スキーマ(schema) として定義
  • そのスキーマを React Hook Form の resolver に渡す
  • スキーマを変更すれば、フォーム側のチェックも自動的に追従

Zod のような型付きスキーマと組み合わせれば、

  • 「スキーマ → TypeScript の型 → フォーム」 という流れで
    「型」と「バリデーション」と「フォーム構造」が揃った、
    スキーマ駆動フォーム に発展させることもできます。

2. UI コンポーネントライブラリと組み合わせた「きれいで壊れにくいフォーム」

MUI / Chakra UI / Mantine などの UI ライブラリは、見た目が整った入力コンポーネントを提供してくれますが、多くは「controlled component」になっています。
React Hook Form の Controller / useController を使うと、こうしたコンポーネントともスムーズに接続できます。

  • Controllervalue / onChange / onBlur などを橋渡ししてくれる
  • UI ライブラリ側は「いつも通り使うだけ」で、裏側の値管理は React Hook Form に任せられる
  • サードパーティ用の公式/コミュニティ製バインディング集(Material UI, Chakra, AntD など)も用意されている

結果として、

  • デザインは UI ライブラリに任せる
  • 入力値の管理・バリデーションは React Hook Form に任せる

という きれいな責務分離 ができ、「見た目も中身も強いフォーム」が作りやすくなります。

3. データフェッチ・状態管理と組み合わせた「実務的な画面フロー」

React Hook Form はあくまで「フォームの中」の責務なので、
送信後の処理は TanStack Query や Zustand / Redux などと組み合わせて設計します。

例:

  • handleSubmit の中で TanStack Query の mutate を呼んで API に POST する
  • 送信中は isSubmitting でボタンを disabled にして二重送信防止
  • 成功時は React Router で別ページへ遷移 / モーダルを閉じる
  • 失敗時はトーストライブラリ(react-hot-toast など)でエラーメッセージ表示

こうすることで、「フォーム → API → 画面遷移」まで一続きの UX を作れます。

4. API スキーマやコードジェネレータと組み合わせた「フロント〜バックの型安全」

OpenAPI / tRPC / GraphQL などと、Zod や TypeScript の型生成を組み合わせると、

  1. バックエンドの API スキーマから型や Zod スキーマを自動生成
  2. 生成されたスキーマを zodResolver で React Hook Form に渡す
  3. バックエンドとフロントのバリデーションルール&型が自動で同期

という構成も取れます。

結果的に、

  • 仕様変更があっても「スキーマ → 型 → フォーム」が一気に更新される
  • 「バックエンドでは NG なのにフロントでは通ってしまう」といったズレを減らせる

という 型安全・仕様同期されたフォーム実装 に近づけます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?