ReactでのZodを使ったフォームバリデーションの基礎と実践

## はじめに
今回はZodを用いたReactのバリデーションの基礎と実践について記事を書きました。
業務で使うことが非常に多いので、基礎を学びながら学習したことをアウトプットしています。

## Zodとは何か
Zodは、JavaScriptおよびTypeScriptのための型 **セーフなスキーマ定義とバリデーションライブラリ** です。
Zodを使用すると、**データの構造や形式を明示的に定義** し、その**スキーマに基づいてデータをバリデートする**ことができます。
これにより、データの一貫性を保ち、バグを減らすことができます。

## なぜZodを選ぶのか
### 型セーフティ
TypeScriptとの統合がスムーズで、型情報を利用してコードの安全性を向上させます。
簡潔なAPI：直感的なAPIにより、複雑なスキーマも簡単に定義できます。

### 柔軟性
カスタムバリデーションや複雑な構造のデータにも対応可能です。

### パフォーマンス
軽量で高速なバリデーションを提供しています。

## 導入

### Zodのインストール - 必要なライブラリのインストール手順
ZodをReactアプリケーションで使用するためには、まず必要なライブラリをインストールします。

```:コマンド
// npm
npm install zod react-hook-form

// yarn
yarn add zod react-hook-form
```

### 基本的な使用例 - シンプルなスキーマの定義

Zodを使用してシンプルなスキーマを定義します。
ここでは、ユーザー情報をバリデートするスキーマを作成します。

```react:validation.ts
import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(1, "名前は必須です"),
  email: z.string().email("有効なメールアドレスを入力してください"),
  age: z.number().min(18, "18歳以上である必要があります"),
});
```

#### 使用したZodのメソッド

##### .min()
**数値や文字列の最小値を指定する** ために使われます。
具体的には、**文字列の場合は最小文字数**を、数値の場合は**最小値**を設定します。

今回のバリデーションでは、nameSchemaは**1文字以上の文字列である必要**があります。1文字未満の場合は、エラーメッセージ「名前は必須です」が表示されます。

また、ageSchemaは **18以上の数値である必要**があります。18未満の場合は、エラーメッセージ「18歳以上である必要があります」が表示されます。

##### .email()
**文字列が有効なメールアドレス形式であるか**どうかをバリデーションするために使われます。.email()メソッドを使うことで、ユーザーが入力したメールアドレスは適切な形式になっているかをチェックできます。


### データのバリデーション
定義したスキーマを使用してデータをバリデートします。

```react:form.tsx
const userData = {
  name: "Taro",
  email: "taro@example.com",
  age: 20,
};

const result = userSchema.safeParse(userData);

if (result.success) {
  console.log("バリデーション成功:", result.data);
} else {
  console.log("バリデーション失敗:", result.error.errors);
}
```

#### メソッドについて
##### safeParse
与えられたデータをスキーマに基づいてバリデーションし、その結果を安全に扱うためのものです。safeParseメソッドは、**バリデーションの成功・失敗に関わらず、常にオブジェクトを返します**。
このオブジェクトには、バリデーションが成功したかどうかを示すsuccessプロパティと、成功した場合はバリデートされたデータ、失敗した場合はエラーメッセージが含まれます。

###### safeParseの利点

:::note info
**安全なエラーハンドリング**
バリデーションの結果をオブジェクトとして返すため、**一貫した結果** : **try-catchブロックを使用せずにバリデーションエラーを安全に処理** できます。
:::

:::note info
**一貫した結果**
バリデーションの成功・失敗に関わらず、**常に同じ形式のオブジェクトを返す**ため、処理がシンプルになります。
:::


### Reactとの統合 - React Hook Formとの連携
次に、React Hook FormとZodを組み合わせてフォームバリデーションを行う方法です。

```react:TestForm.tsx
import React from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(1, "名前は必須です"),
  email: z.string().email("有効なメールアドレスを入力してください"),
  age: z.number().min(18, "18歳以上である必要があります"),
});

const App = () => {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: zodResolver(userSchema)
  });

  const onSubmit = data => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>名前:</label>
        <input {...register('name')} />
        {errors.name && <span>{errors.name.message}</span>}
      </div>
      <div>
        <label>メールアドレス:</label>
        <input {...register('email')} />
        {errors.email && <span>{errors.email.message}</span>}
      </div>
      <div>
        <label>年齢:</label>
        <input type="number" {...register('age')} />
        {errors.age && <span>{errors.age.message}</span>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default App;
```

#### ライブラリー・オブジェクト・関数 について
##### zodResolver
React Hook FormとZodを統合するためのユーティリティ関数です。
**React Hook Form**は、**フォームの状態管理とバリデーションをシンプルに行うためのライブラリ**で、zodResolverを使用することで、Zodスキーマを使ったバリデーションを**簡単にReact Hook Formに組み込む**ことができます。

##### useForm
**React Hook Formライブラリの主要なフック**で、**フォームの状態管理とバリデーションを行うために使用**されます。
このフックを使用することで、**フォームの入力値、バリデーションエラーメッセージ、フォームの送信処理などを簡単に管理**できます。

##### register
フォームの入力フィールドを**React Hook Formに登録するための関数**です。
これを使用することで、**フォームのデータバインディングとバリデーションが自動的に管理**されます。registerを使用して入力フィールドを登録することで、React Hook Formがその**フィールドの値を追跡し、バリデーションを適用**できます。

##### handleSubmit
フォームの**送信処理**を行うための関数です。
フォームが送信されたときに、バリデーションを実行し、**バリデーションが成功した場合に指定されたコールバック関数を呼び出し**ます。失敗した場合は、**バリデーションエラーがformState.errorsに格納**されます.

###### コールバック関数

:::note info
**errors**
各フィールドのバリデーションエラーを保持するオブジェクト。
:::

:::note info
**isSubmitting**
フォームが送信中であるかどうかを示すブール値
:::

:::note info
**isDirty**
フォームに変更があったかどうかを示すブール値
:::

:::note info
**isValid**
フォームがバリデーションに合格しているかどうかを示すブール値
:::

#### onSubmit
フォームが**送信されたときに呼び出されるコールバック関数**です。
React Hook Formでは、**handleSubmit関数と組み合わせて使用**されます。

handleSubmitは、フォーム送信時にバリデーションを実行し、バリデーションが**成功した場合にonSubmit関数**を呼び出します。

##### formState
**フォームの状態に関する情報を提供するオブジェクト**です。
formStateには、**フォームのエラー、送信状態、変更状態など**が含まれます。
主に**バリデーションエラーの表示**や**フォームの送信状態の管理**に使用されます。

#### handleSubmit
React Hook Formの関数で、**フォームが送信されたときにバリデーションを実行**し、バリデーションが**成功した場合に指定したコールバック関数（通常はonSubmit関数）を呼び出し**ます。
handleSubmitは、フォームの送信処理を簡素化し、**バリデーションエラーの管理を自動化するた**めに使用されます。

### 実装内容
上記の例では**React Hook FormとZodを使用してフォームのバリデーション**を行い、エラーメッセージを表示しています。
各入力フィールドに対してerrorsオブジェクトをチェックし、該当するエラーメッセージを表示することで、ユーザーに適切なフィードバックを提供します。

### Zodのよくある使用方法
Zod、React Hook Form、React Queryを組み合わせて使用されます。
今回は **React Query** を用いた例ではありませんがいずれは紹介したいと思います。

:::note info
**Zod**
**スキーマベースのバリデーションライブラリ**で、データの構造を定義し、そのデータが期待される形式に従っているかどうかを**チェック**します。
:::

:::note info
**React Hook Form**
**フォームの状態管理ライブラリ**で、**フォームの入力値、バリデーション、送信処理を簡単に扱う**ことができます。
:::

:::note info
**React Query**
**サーバーからのデータ取得、キャッシュ、同期を効率的に行うライブラリ**です。
:::

## まとめ
Zodは、Reactアプリケーションにおけるフォームバリデーションをシンプルかつ強力にするツールです。
TypeScriptとの相性も良く、複雑なバリデーション要件にも柔軟に対応できます。
React Hook Formと組み合わせることで、**直感的かつ堅牢なフォームバリデーションを実現**することができます。ぜひ、Zodを活用して、より信頼性の高いアプリケーションを構築してください。

## さいごに
最後まで、お読みいただきありがとうございました。
勉強しながら記事を投稿できたらいいな‥!!と思っています。


## リンク

https://zod.dev/

https://tanstack.com/query/latest/docs/framework/react/overview

https://www.react-hook-form.com/