Reactでフォームを扱おうとすると、毎回useStateで状態を管理して、バリデーションを書いて、エラー表示して…
これ、地味に手間。
React Hook Form(略:RHF)を使えば、それらを最小のコードで綺麗に書けるようになる。
useForm() でフォームを初期化register() でフォーム要素に紐づけhandleSubmit() で送信処理errors でエラーメッセージ管理tsx
const {
register,
handleSubmit,
formState: { errors },
} = useForm();
tsx
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register("username", { required: "ユーザー名は必須です" })} />
{errors.username && <p>{errors.username.message}</p>}
<button type="submit">ログイン</button>
</form>
Zodを使えば、スキーマ(構造)とバリデーションを同時に定義できる。
tsx
const schema = z.object({
username: z.string().min(1, "ユーザー名は必須です"),
password: z.string().min(6, "6文字以上で入力してください"),
});
useFormにスキーマを渡す:
tsx
const {
register,
handleSubmit,
formState: { errors },
} = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
});
RHFとMUIを組み合わせるときは、TextFieldに{...register("name")}を渡すだけでOK。
tsx
<TextField
label="ユーザー名"
{...register("username")}
error={!!errors.username}
helperText={errors.username?.message}
/>
errorでエラーステートを制御helperTextでエラーメッセージ表示Tailwindを使うと、柔軟なデザインをそのまま適用できる:
tsx
<input
{...register("keyword", { required: "キーワードを入力してください" })}
className="border p-2 w-full"
/>
{errors.keyword && (
<p className="text-red-500 text-sm mt-1">{errors.keyword.message}</p>
)}
今回のサンプルでは、以下をRHFで実装:
👇 サンプル:RHFログイン・検索フォームアプリ
| 項目 | useState手書き | React Hook Form |
|---|---|---|
| 状態管理 | 1項目ずつ必要 | 自動でやってくれる |
| バリデーション | 自前で実装 | オプションで宣言的に書ける |
| エラー表示 | 自前ロジック | errorsを見るだけ |
| 再レンダリングコスト | 多い | 少ない(Controllerベース) |
| スケーラビリティ | 面倒になる | 大規模でも対応しやすい |