はじめに
React学習のアウトプットで、React Hook FormとZodについてまとめる。
React Hook Formとは
概要
Reactのための軽量で高パフォーマンスなフォーム管理ライブラリ。
フォームの状態管理やバリデーションを簡潔に実装できる。
特徴
- フォーム状態の自動管理
- 入力値、エラー状態、バリデーションの状態を内部で効率的に管理
- 開発者が状態を手動で設定する必要がない
- パフォーマンス
- 入力フィールドの状態管理が個別に行われるため、全体の再レンダリングを最小限に抑える
- 特に、大規模なフォームでパフォーマンスの違いが顕著
- 柔軟なバリデーション
- React Hook Form自体でシンプルなバリデーション(例: 必須チェック)をサポート
- 外部バリデーションライブラリ(e.g., Zod, Yup)と統合することで、高度なバリデーションも可能
- 軽量
- サイズが小さく、依存関係が少ないため、パフォーマンスへの影響が少ない
Zodとは
概要
スキーマ定義とバリデーションを行うためのTypeScript対応ライブラリ。
スキーマをもとに入力データを検証し、型安全なプログラミングを可能にする。
特徴
- スキーマ定義とバリデーション
- Zodを使って、データの構造(スキーマ)とルールを定義可能
- 例: ユーザー名は文字列、メールは有効な形式、など
- Zodを使って、データの構造(スキーマ)とルールを定義可能
- TypeScriptと統合
- スキーマから型を自動生成できるため、型定義の重複を防止
- フォームデータの型安全性を高める
- 柔軟で強力なバリデーション
- 入力値に対して簡単な条件(必須、最大文字数など)から複雑なルールまで適用可能
- 簡単なAPI
- シンプルな構文でスキーマを作成でき、直感的に使える
React Hook Form × Zodの連携
React Hook Formのフォーム管理機能とZodの強力なバリデーション機能を組み合わせることで、型安全で高パフォーマンスなフォームを実現することができる
連携の流れ
- スキーマ定義
- Zodで入力フィールドのバリデーションルールを定義
- Zodを
resolver
として設定- React Hook Formの
useForm
でzodResolver
を指定し、Zodのバリデーションを統合
- React Hook Formの
- フォームの送信時にバリデーションを実行
- Zodが定義されたスキーマを使い、入力値を検証
- エラー時はReact Hook Formがエラー状態を管理
連携のメリット
- 型安全性
- フォームデータの型定義とバリデーションルールが同期
- 簡潔なコード
- フォーム管理とバリデーションを分離しつつ統合的に運用
- 拡張性
- 大規模プロジェクトでも柔軟に対応可能
実際に使ってみる
今回使用する技術
- React 18
- TypeScript
- Vite
- Zod
- @hookform/resolvers
プロジェクトのセットアップ
1-1. Viteプロジェクトの作成
npm create vite@latest my-form-app --template react-ts
cd my-react18-app
1-2. React 18をインストール
npm install react@18 react-dom@18
1-3. 必要な依存関係をインストール
npm install react-hook-form @hookform/resolvers zod
フォームを実装
今回は3つの入力フォームを用意する。
import React from "react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
// Zodスキーマを定義
const schema = z.object({
username: z.string().min(3, { message: "ユーザー名は3文字以上で入力してください" }),
email: z.string().email({ message: "有効なメールアドレスを入力してください" }),
password: z.string().min(6, { message: "パスワードは6文字以上で入力してください" }),
});
// 型を自動生成
type FormData = z.infer<typeof schema>;
function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = (data: FormData) => {
console.log("送信されたデータ:", data);
};
return (
<div style={{ maxWidth: "400px", margin: "0 auto", fontFamily: "Arial, sans-serif" }}>
<h1>フォーム入力</h1>
<form onSubmit={handleSubmit(onSubmit)}>
{/* ユーザー名 */}
<div>
<label>ユーザー名:</label>
<input {...register("username")} />
{errors.username && <p style={{ color: "red" }}>{errors.username.message}</p>}
</div>
{/* メールアドレス */}
<div>
<label>メールアドレス:</label>
<input type="email" {...register("email")} />
{errors.email && <p style={{ color: "red" }}>{errors.email.message}</p>}
</div>
{/* パスワード */}
<div>
<label>パスワード:</label>
<input type="password" {...register("password")} />
{errors.password && <p style={{ color: "red" }}>{errors.password.message}</p>}
</div>
<button type="submit" style={{ marginTop: "10px" }}>送信</button>
</form>
</div>
);
}
export default App;
開発サーバーを起動
以下のコマンドを実行してアプリケーションを起動。
npm run dev
動作確認
- 正常な入力
- フィールドに正しい値を入力し、「送信」ボタンを押すと、コンソールにフォームデータが出力される
{
"username": "testuser",
"email": "test@example.com",
"password": "password123"
}
- 不正な入力
- バリデーションエラーが発生すると、各フィールドの下にエラーメッセージが表示される
Tips:リアルタイムでバリデーションを行うには
useForm
のmode
オプションを変更する。
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(schema),
mode: "onChange", // 入力中にバリデーションを行う
});