5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

前回の記事では Zod 4 の変更点をまとめました。

今回はその Zod 4 と React Hook Form を組み合わせて、実際に「ログインフォーム」を作ってみます。

Zod とは

Zod とは、TypeScript 向けのスキーマ定義・バリデーションライブラリです。スキーマを定義すると、そのまま TypeScript の型にも反映できるのが大きな特徴です。

React Hook Form とは

React Hook Form は、React のフックをベースにしたフォーム管理ライブラリです。 公式の @hookform/resolvers を使うと、Zod といったバリデーションライブラリを簡単に組み合わせられます。

Zod と React Hook Form を組み合わせるメリット

  • 型定義とバリデーションの一元化
    • Zod のスキーマ定義だけで、型とバリデーションルールを同時に管理できる
  • スキーマ通りのフォーム実装
    • React Hook Form にスキーマ由来の型を渡すと、入力欄の名前や型がスキーマどおりに保たれる
      • エディタのオートコンプリートが効き、存在しないフィールド指定でコンパイルエラーになる
  • 安全なデータ受け渡し
    • スキーマから推論した型を使うことで、フォーム間や API 呼び出し時に常にスキーマどおりのデータを扱える

TypeScript x Zod x React Hook Form でログインフォームを作る

今回は、メールアドレスとパスワード、確認用のパスワードを入力するログインフォームを用意します。

環境

CodeSandbox の React(TS)テンプレートで動作確認しています。

スキーマを定義する

まずはスキーマを定義します。

schema.ts
import { z } from 'zod';

// 数字を含むことをチェックする正規表現
export const numberPattern = /(?=.*\d)/;

export const schema = z
  .object({
    // z.email() は、Zod 4 で導入された記法
    email: z.email('正しいメールアドレスを入力してください'),
    // パスワードは、16文字以上で数字を含むように
    password: z
      .string()
      .min(16, '16文字以上で入力してください')
      .regex(numberPattern, '数字を含めてください'),
    confirm: z.string(),
  })
  // パスワードと確認用のものが一致しているかを確認する
  .refine((data) => data.password === data.confirm, {
    path: ['confirm'],
    message: 'パスワードが一致しません',
  });

// スキーマに沿った型を生成
export type FormData = z.infer<typeof schema>;

フォームを実装する

実際にフォームを実装してみます。ただ単にメールアドレスやパスワードを入力できるフォームだと味気なかったので、入力内容を watch して表示を変更できるようにしてみました。

App.tsx
import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { numberPattern, schema, FormData } from './schema';

const App: React.FC = () => {
  const {
    watch,
    register,
    handleSubmit,
    formState: { errors },
  } = useForm<FormData>({
    // Zod で定義したスキーマをバリデーションに適用する
    resolver: zodResolver(schema),
    defaultValues: {
      email: '',
      password: '',
      confirm: '',
    },
  });

  // 入力内容によって表示を変更できるように、パスワードのフォームの内容を watch する
  const password = watch('password');
  // パスワードが 16 文字以上か
  const isLongEnough = password.length >= 16;
  // パスワードに数字を含んでいるかどうか
  const hasNumber = numberPattern.test(password);

  const onSubmit = (data: FormData) => {
    // 今回は特に保存などしないので、console にデータを表示する
    console.log('送信データ:', data);
  };

  return (
    <div>
      <h1>ログインフォーム</h1>
      <form onSubmit={handleSubmit(onSubmit)}>
        <div>
          <label>メールアドレス</label>
          <input
            type="email"
            {...register('email')}
            style={{ display: 'block' }}
          />
          {errors.email && (
            <p style={{ color: 'red' }}>{errors.email.message}</p>
          )}
        </div>

        <div>
          <label>パスワード</label>
          <input
            type="password"
            {...register('password')}
            style={{ display: 'block' }}
          />
          <ul style={{ listStyle: 'none', padding: 0 }}>
            {/* 入力内容によって表示を変更する */}
            <li>{isLongEnough ? '' : '🚫'} 16文字以上</li>
            <li>{hasNumber ? '' : '🚫'} 数字を含む</li>
          </ul>
          {errors.password && (
            <p style={{ color: 'red' }}>{errors.password.message}</p>
          )}
        </div>

        <div>
          <label>パスワード(確認用)</label>
          <input
            type="password"
            {...register('confirm')}
            style={{ display: 'block' }}
          />
          {errors.confirm && (
            <p style={{ color: 'red' }}>{errors.confirm.message}</p>
          )}
        </div>

        <button type="submit" style={{ marginTop: '12px' }}>
          送信
        </button>
      </form>
    </div>
  );
};

export default App;

動作イメージ

さいごに

今回は Zod 4 と React Hook Form で基本的なログインフォームを作成しました。

次回は、もっと複雑なフォームやカスタムバリデータ、エラー表示のカスタマイズ例などについて掘り下げていきたいと思います。

5
2
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
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?