【React修行日記】React Hook Form の導入
学習の目的
- React Hook Formを使用してフォームを実装できるようになる
- フォームのバリデーション設定方法を理解する
React Hook Formとは
React Hook Formは、Reactでフォームをシンプルかつ型安全に扱うためのライブラリ。
フォームの入力値・エラー・バリデーション状態などをReactの状態管理に組み込むことなく、軽量に扱えるのが特徴。
主な特徴:
- 再レンダーが少ない(パフォーマンスが良い)
- コードがスッキリ(stateやonChangeが不要)
- バリデーションが直感的に書ける
- TypeScriptとの相性が良い(型安全なフォームが書ける)
実装ステップ
今回は簡易的なユーザー登録フォームをReact Hook Formを利用して実装してみた...!

①フォームの土台を作る
※Tailwind CSSとshadcn/uiを使用。
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
export default function UserForm() {
return (
<form className="flex flex-col gap-6 mx-auto max-w-[520px]">
<h2 className="font-bold">ユーザー登録フォーム</h2>
<div className="flex flex-col gap-3">
<Label htmlFor="name">名前</Label>
<Input type="text" id="name" placeholder="例:山田太郎" />
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="email">メールアドレス</Label>
<Input type="email" id="email" placeholder="例:example@example.com" />
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="age">年齢</Label>
<Input type="text" id="age" placeholder="例:25" />
</div>
<Button>登録する</Button>
</form>
);
}
②useForm()を導入
まず、React Hook Formをプロジェクトへ追加。
npm install react-hook-form
次に、react-hook-formからuseFormをimportしてフォームの状態を管理できるようにする。
useForm()はReact Hook Formの中核となる関数で、「入力値」「エラー」「バリデーション」「送信処理」などをひとまとめに扱えるようにする。
import { useForm } from "react-hook-form";
type FormData = {
name: string;
email: string;
age: string;
};
export default function UserForm() {
const { register, handleSubmit } = useForm<FormData>();
const onSubmit = (data: FormData) => {
console.log(data);
};
return (
<form
onSubmit={handleSubmit(onSubmit)}
className="flex flex-col gap-6 mx-auto max-w-[520px]"
>
型の定義
フォームで扱うデータの型を定義しておくと、register()などで入力項目を紐付けたときに型補完やエラー防止が効くようになる。
type FormData = {
name: string;
email: string;
age: string;
};
useForm()の呼び出し
useForm()を呼び出すと、フォームの状態管理に必要なさまざまな関数や値をまとめて取得できる。
ここで<FormData>のように型を指定することで、フォームの入力項目に型安全性を持たせられる。
const { register, handleSubmit } = useForm<FormData>();
▽ここでは主に2つの関数を使用
| 関数名 | 役割 |
|---|---|
register |
各入力フィールドをReact Hook Formに登録(管理対象にする) |
handleSubmit |
送信イベント時にフォーム全体を検証&データを渡す |
onSubmit関数を定義
handleSubmit()は、フォーム送信時に呼び出され、バリデーションが通っていれば、onSubmit関数に入力データを渡す。
フォームタグのonSubmitにhandleSubmit(onSubmit)を渡すだけで、入力→送信→バリデーション→データ受け取り、の流れがすべて自動で処理される。
const onSubmit = (data: FormData) => {
console.log(data);
};
<form onSubmit={handleSubmit(onSubmit)}>
③register()で入力フィールドをReact Hook Formに接続
Inputコンポーネントにregisterを渡して、React Hook Formにデータを管理させる。
<Input type="text" id="name" placeholder="例:山田太郎" {...register("name")} />
<Input type="email" id="email" placeholder="例:example@example.com" {...register("email")} />
<Input type="text" id="age" placeholder="例:25" {...register("age")} />
register("name")を展開(スプレッド)して渡すことで、この<Input>が「name」というフィールドとしてReact Hook Formに登録される。
これだけで以下のようなことが自動で行われる。
- 入力値の取得・監視
- 値の変更による再レンダー制御
- バリデーションのトリガー管理
ここまでで、フォームの各入力フィールド(name・email・age)がReact Hook Form に登録され、入力内容を自動で管理できる状態になった。
フォーム送信時にはhandleSubmit(onSubmit)によって、入力データがまとめてdataオブジェクトとして取得可能。
④バリデーション(必須チェック&エラーメッセージ表示)
次に、フォームに入力ルール(バリデーション)を追加して、未入力や不正な値を防ぐ処理を追加する。
formState.errorsでエラー情報を取得
useFormからformState: { errors }を取り出すことで、各入力項目に発生したエラー情報へアクセスできる。
const {
register,
handleSubmit,
formState: { errors }, //追加
} = useForm<FormData>();
たとえば、errors.nameには「名前」フィールドのエラー内容が格納され、errors.name?.messageでエラーメッセージを取得できる。
requiredで必須チェックを追加
必須項目としたいInputコンポーネントのregister()の第2引数にバリデーションルールを指定する。
ここではrequiredにエラーメッセージを設定。
{errors.name && (
<p className="text-left text-sm text-red-500">{errors.name.message}</p>
)}
<Input
type="text"
id="name"
placeholder="例:山田太郎"
{...register("name", { required: "名前は必須です" })} //追加
/>
⑤バリデーション強化
文字数制限
名前フィールドに文字数制限(例:2〜20文字)を追加。
{...register("name", {
required: "名前は必須です",
minLength: { value: 2, message: "2文字以上入力してください" }, //追加
maxLength: { value: 20, message: "20文字以内で入力してください" }, //追加
})}
-
minLength:入力文字数の最小値 -
maxLength:入力文字数の最大値 -
message:ルールに違反した場合に表示するエラーメッセージ
正規表現チェック
年齢フィールドに半角数字チェックを追加。
{...register("age", {
pattern: {
value: /^[0-9]+$/,
message: "半角数字で入力してください",
},
})}
-
value:正規表現(RegExp) -
message:違反時のエラーメッセージ
▽register関数のオプション
⑥UI/UX改善
リアルタイムバリデーション
通常、React Hook Formのバリデーションは送信時にのみ実行される。
ユーザーにとっては、入力中に間違いを指摘してもらえるほうがわかりやすい場合があるので、今回は入力中にエラーが即時表示されるように実装。
useForm<FormData>({
mode: "onChange",
});
-
mode: "onChange"を指定すると、入力値が変更されるたびにバリデーションが実行される - エラー表示や
isValidの状態も即時更新されるので、ユーザーは間違いをすぐに修正可能
▽modeの指定方法
| モード | 説明 |
|---|---|
onSubmit(デフォルト) |
フォーム送信時にバリデーションを実行 |
onChange |
入力値が変わるたびバリデーションを実行 |
onBlur |
入力欄からフォーカスが外れたタイミングでバリデーション |
all |
onChange と onBlur 両方でバリデーションを実行 |
登録ボタンの制御
バリデーションがすべて通ったときだけボタンを活性化して、送信可能状態かどうかをわかりやすくすることもできる。
const { register, handleSubmit, formState: { errors, isValid } } = useForm<FormData>({
mode: "onChange",
});
:
<Button type="submit" disabled={!isValid}>
登録する
</Button>
-
mode: "onChange"にすることで、入力があるたびisValidが更新される -
isValidは必須項目とバリデーションルールを全て通っているかを判定する真偽値
▽formStateの主なプロパティ
| プロパティ名 | 説明 |
|---|---|
errors |
各フィールドのバリデーションエラー情報 |
isDirty |
1度でも入力値が変更されたか の真偽値 |
dirtyFields |
変更されたフィールドの一覧 |
isSubmitting |
フォーム送信中か の真偽値(非同期送信時に便利) |
isSubmitted |
一度でも送信されたか の真偽値 |
submitCount |
フォームが送信された回数 |
isValidating |
現在バリデーション中かどうか |
touchedFields |
フォーカスアウト(blur)したフィールドの一覧 |
isValid |
全ての必須チェック・バリデーションを通過しているか の真偽値 |
▽その他のプロパティ
まとめ
- React Hook Formを使用することで、簡単にフォームを作成することができる。
- 必須チェックや文字数制限などのバリデーション設定によって、UI/UXを意識したフォームを作成できる。
参考
Discussion