react-hook-form + Zod でフォームバリデーション入門

スポンサーリンク

react-hook-form + Zod でフォームバリデーション入門

react-hook-formzod を組み合わせると、フォームのバリデーションとエラーメッセージ表示を型安全に実装できます。zodResolver がスキーマとフォームをつなぐ役割を担います。


早わかりまとめ

役割 パッケージ
フォーム管理 react-hook-form
スキーマ定義・型生成 zod
橋渡し @hookform/resolvers/zodzodResolver

インストール

npm install react-hook-form zod @hookform/resolvers

基本パターン

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'

// ① スキーマ定義
const schema = z.object({
  email: z.string().email({ message: 'メールアドレスを入力してください' }),
  password: z.string().min(8, { message: '8文字以上で入力してください' }),
})

// ② スキーマから型を生成
type FormValues = z.infer<typeof schema>

const SignInForm = () => {
  const {
    register,
    handleSubmit,
    formState: { errors, isValid },
  } = useForm<FormValues>({
    // ③ zodResolver でスキーマを渡す
    resolver: zodResolver(schema),
    mode: 'onChange', // 入力のたびにバリデーション
  })

  const onSubmit = (data: FormValues) => {
    // data は schema を通過済みなので型安全
    console.log(data)
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <input type="email" placeholder="Email" {...register('email')} />
        {errors.email && <p>{errors.email.message}</p>}
      </div>

      <div>
        <input type="password" placeholder="Password" {...register('password')} />
        {errors.password && <p>{errors.password.message}</p>}
      </div>

      <button type="submit" disabled={!isValid}>ログイン</button>
    </form>
  )
}

mode の違い

useForm({ mode: 'onChange' })   // 入力のたびにバリデーション
useForm({ mode: 'onBlur' })     // フォーカスが外れたときにバリデーション
useForm({ mode: 'onSubmit' })   // submit 時にのみバリデーション(デフォルト)

複雑なスキーマ例

const profileSchema = z.object({
  name: z.string().min(1, { message: '名前を入力してください' }),
  age: z.number({ invalid_type_error: '数値を入力してください' }).min(0).max(150),
  website: z.string().url({ message: 'URLの形式で入力してください' }).optional(),
  role: z.enum(['admin', 'user', 'guest']),
})

type ProfileValues = z.infer<typeof profileSchema>

defaultValues でデフォルト値を設定する

useForm<FormValues>({
  resolver: zodResolver(schema),
  defaultValues: {
    email: '',
    password: '',
  },
})

watch でリアルタイムに値を取得する

const { register, watch } = useForm<FormValues>({ resolver: zodResolver(schema) })
const emailValue = watch('email')

// emailValue が変わるたびに再レンダリング

reset でフォームをリセットする

const { reset, handleSubmit } = useForm<FormValues>({ resolver: zodResolver(schema) })

const onSubmit = (data: FormValues) => {
  // 送信後にリセット
  reset()
}

まとめ

やること コード
スキーマ定義 z.object({ ... })
型生成 type T = z.infer<typeof schema>
フォームに組み込む resolver: zodResolver(schema)
エラー表示 errors.フィールド名?.message
送信可否の制御 disabled={!isValid}
バリデーションタイミング mode: 'onChange' など

Zod の基本的なスキーマ定義については「Zod 入門:z.object・z.infer・safeParse の使い方」を参照してください。