Reactでカスタムバリデーションロジックを実装する方法を徹底解説

Reactでのフォームバリデーションは、ユーザー入力を正確に検証し、エラーを未然に防ぐための重要な要素です。特に、標準的なバリデーションでは対応できないユニークな要件がある場合、カスタムバリデーションロジックの実装が求められます。本記事では、Reactを使用してカスタムバリデーションを構築する方法をステップバイステップで解説します。これにより、複雑な入力チェックをスムーズに行い、ユーザーフレンドリーなフォームを作成するためのスキルを習得できるでしょう。

目次
  1. カスタムバリデーションロジックの概要
    1. カスタムバリデーションの主な目的
    2. 利用ケース
  2. React標準バリデーションとの違い
    1. 標準バリデーションの特徴
    2. カスタムバリデーションの特徴
    3. 具体例での違い
    4. 使い分けのポイント
  3. カスタムバリデーションの実装準備
    1. 1. 開発環境の設定
    2. 2. 必要なライブラリのインストール
    3. 3. プロジェクト構成の確認
    4. 4. バリデーションロジックの基本設計
    5. 5. 状態管理の準備
  4. フィールド単位のバリデーションロジック作成
    1. 1. フィールドバリデーションの基本構造
    2. 2. Reactコンポーネントでの利用
    3. 3. リアルタイムバリデーションの実装
    4. 4. 複数条件のバリデーション
    5. 5. フィールドバリデーションのモジュール化
  5. フォーム全体のバリデーション
    1. 1. フォーム全体のバリデーションが必要なケース
    2. 2. フォーム全体の状態管理
    3. 3. フォーム全体の検証ロジック
    4. 4. React Hook Formを使った効率化
    5. 5. フォーム全体のバリデーションの注意点
  6. React Hook Formを使ったバリデーション強化
    1. 1. React Hook Formの基本構造
    2. 2. カスタムバリデーションの実装
    3. 3. 動的なバリデーションロジック
    4. 4. バリデーションスキーマの使用(Yupの導入)
    5. 5. React Hook Formのメリット
  7. よくある問題とトラブルシューティング
    1. 1. バリデーションロジックが機能しない
    2. 2. 複数のエラーメッセージが競合する
    3. 3. バリデーション結果が遅延する
    4. 4. 動的フィールドでエラーが発生する
    5. 5. カスタムバリデーションでエラーハンドリングが不足
    6. 6. ユーザーエクスペリエンスが低下する
    7. 7. デバッグのためのツール
  8. 応用例: 動的フィールドのバリデーション
    1. 1. 動的フィールドの利用ケース
    2. 2. 動的フィールドの基本構造
    3. 3. コードのポイント
    4. 4. 動的フィールドにおけるエラー管理
    5. 5. バリデーションの応用例
    6. 6. ユーザーエクスペリエンスの向上
  9. まとめ

カスタムバリデーションロジックの概要


カスタムバリデーションロジックとは、標準的なバリデーション(例: 必須チェック、文字数制限)では対応できない特定の要件に応じて設計された検証方法です。例えば、メールアドレスの形式だけでなく、特定のドメインのみを許可する場合や、数値の範囲を動的に変更する必要がある場合に用いられます。

カスタムバリデーションの主な目的


カスタムバリデーションの目的は以下の通りです。

  • 動的要件への対応: 固定ルールではなく、状況に応じて変化する検証条件を実現します。
  • 複雑なビジネスロジックの適用: 特定の業務要件に応じた入力ルールを反映します。
  • ユーザーエクスペリエンスの向上: ユーザーが正しい形式で入力できるようリアルタイムでガイドします。

利用ケース

  • パスワード強度チェック: 英数字の組み合わせや特殊文字を含むかどうかを検証する。
  • 日付の相関チェック: 開始日が終了日より早いかどうかを確認する。
  • データベースとの連携: 入力データが既存データと重複していないかを確認する。

これらの特徴を活用することで、Reactフォームの信頼性を大幅に向上させることができます。

React標準バリデーションとの違い

Reactでは標準的なバリデーション手法が利用可能ですが、カスタムバリデーションを取り入れることでより高度で柔軟な検証が可能になります。それぞれの特徴と違いを理解することで、適切なアプローチを選択できます。

標準バリデーションの特徴


Reactの標準バリデーションは、以下のようなシンプルなルールに基づいています。

  • HTML属性による制御: 必須入力(required)、最大値・最小値(minmax)、文字数制限(maxlengthminlength)などを使用。
  • シンプルで設定が容易: 少ない手間で基本的な入力チェックが可能。
  • リアルタイムフィードバックの制限: 直接的なエラー表示の実装は開発者が追加で記述する必要がある。

カスタムバリデーションの特徴


一方、カスタムバリデーションは以下のような特性を持ちます。

  • 柔軟な条件設定: 動的な要件や複雑なビジネスルールに対応。
  • ロジックの再利用性: 独自の関数やモジュールを定義して再利用可能。
  • Reactの状態管理との統合: コンポーネントの状態(useStateuseReducer)やライフサイクルに基づいた検証が可能。

具体例での違い

  • 標準バリデーション:
  <input type="email" required minLength={5} />

ここでは、メール形式かどうか、入力必須かどうか、最低5文字以上かを自動的にチェックします。

  • カスタムバリデーション:
  const validateEmail = (email) => email.endsWith("@example.com");
  const handleBlur = (e) => {
    if (!validateEmail(e.target.value)) {
      alert("ドメインは@example.comのみ許可されています");
    }
  };

  <input type="email" onBlur={handleBlur} />

入力されたメールアドレスが特定のドメインであるかを検証する独自ロジックを追加しています。

使い分けのポイント

  • 簡単な入力チェックが必要な場合は標準バリデーション。
  • ユーザーや状況に応じた高度なチェックが必要な場合はカスタムバリデーション。

これらの違いを理解し、適切に使い分けることで、Reactフォームの検証精度を向上させることができます。

カスタムバリデーションの実装準備

カスタムバリデーションロジックをReactで実装するには、適切な環境を整えることが重要です。ここでは、基本的な準備ステップについて解説します。

1. 開発環境の設定


まず、Reactのプロジェクトが正しくセットアップされていることを確認してください。以下の手順を実行します。

  1. Node.jsのインストール: 最新のLTSバージョンをインストールします。
  2. Reactアプリの作成: Create React AppやViteを使ってプロジェクトを初期化します。
   npx create-react-app custom-validation
   cd custom-validation

2. 必要なライブラリのインストール


カスタムバリデーションを効率よく実装するために、以下のライブラリをインストールすることをお勧めします。

  • React Hook Form(推奨): バリデーションとフォーム状態管理を簡略化します。
   npm install react-hook-form
  • Yup(オプション): スキーマベースのデータバリデーションをサポートします。
   npm install yup @hookform/resolvers

3. プロジェクト構成の確認


カスタムバリデーションロジックの設計を効率化するため、プロジェクトの構成を以下のように整えます。

src/
├── components/
│   ├── Form.js          # フォームコンポーネント
│   ├── ValidationUtils.js # バリデーションロジックを集約
├── App.js
├── index.js
  • Form.js: 実際のフォームUIとバリデーションロジックを統合するためのコンポーネント。
  • ValidationUtils.js: バリデーション関数をモジュール化し、再利用性を高める。

4. バリデーションロジックの基本設計


以下のような基本的な関数を設計します。

  • 単一フィールドのチェック
  export const validateEmail = (email) => {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    return emailRegex.test(email);
  };
  • 複数条件のチェック
  export const validatePassword = (password) => {
    return (
      password.length >= 8 &&
      /[A-Z]/.test(password) &&
      /[0-9]/.test(password)
    );
  };

5. 状態管理の準備


Reactの状態管理を利用して、入力値やエラーメッセージを管理します。

  • useState: シンプルな状態管理に使用します。
  • useReducer: フォームの状態が複雑な場合に使用します。

これらの準備を整えることで、カスタムバリデーションロジックの構築がスムーズに進行します。次のステップで実際の実装に取り掛かります。

フィールド単位のバリデーションロジック作成

個々のフォームフィールドに適用するカスタムバリデーションロジックは、特定の条件を満たすかを確認する重要な役割を担います。このセクションでは、Reactでフィールド単位のバリデーションを実装する方法を解説します。

1. フィールドバリデーションの基本構造


バリデーションロジックを単一の関数として定義し、それを特定のフィールドに適用します。例えば、以下のようにメールアドレスのバリデーションを実装できます。

export const validateEmail = (email) => {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email) ? '' : '無効なメールアドレスです';
};

この関数は、エラーメッセージを返すことで問題の有無を示します。

2. Reactコンポーネントでの利用


以下は、メールアドレスフィールドにバリデーションを適用するコンポーネントの例です。

import React, { useState } from 'react';
import { validateEmail } from './ValidationUtils';

const EmailField = () => {
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');

  const handleBlur = () => {
    const validationError = validateEmail(email);
    setError(validationError);
  };

  return (
    <div>
      <label htmlFor="email">メールアドレス</label>
      <input
        type="text"
        id="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        onBlur={handleBlur}
      />
      {error && <p style={{ color: 'red' }}>{error}</p>}
    </div>
  );
};

export default EmailField;

3. リアルタイムバリデーションの実装


入力のたびにリアルタイムでバリデーションを実施する場合は、onBlurではなくonChangeでエラーチェックを行います。

const handleChange = (e) => {
  const value = e.target.value;
  setEmail(value);
  setError(validateEmail(value));
};

リアルタイムバリデーションはユーザー体験を向上させますが、入力中のエラー表示が煩わしくなる場合もあるため、適用条件を検討してください。

4. 複数条件のバリデーション


複数の条件を組み合わせる場合、次のようにチェックします。

export const validatePassword = (password) => {
  const errors = [];
  if (password.length < 8) errors.push('8文字以上必要です');
  if (!/[A-Z]/.test(password)) errors.push('少なくとも1つの大文字を含めてください');
  if (!/[0-9]/.test(password)) errors.push('少なくとも1つの数字を含めてください');
  return errors.length > 0 ? errors.join(', ') : '';
};

このロジックをPasswordFieldコンポーネントで利用し、エラーメッセージを表示します。

5. フィールドバリデーションのモジュール化


再利用性を高めるため、バリデーション関数を専用モジュールにまとめると効率的です。また、PropTypesTypeScriptを使うことで、バリデーション関数の引数や戻り値の型を明示するのも良い方法です。

これらを活用することで、堅牢で再利用可能なフィールドバリデーションロジックを構築できます。次に、フォーム全体のバリデーション方法について詳しく解説します。

フォーム全体のバリデーション

フォーム全体を対象としたバリデーションは、複数のフィールドが相互に依存している場合や、全体の整合性を確認したい場合に必要です。このセクションでは、Reactでフォーム全体のバリデーションロジックを実装する方法を解説します。

1. フォーム全体のバリデーションが必要なケース


フォーム全体のバリデーションが必要となる例をいくつか挙げます。

  • 開始日と終了日の相関チェック: 終了日が開始日より前でないかを確認する。
  • 合計値の検証: 入力された数値フィールドの合計が特定の値以下であることをチェックする。
  • すべての必須フィールドが埋められているかの確認

これらの場合、単一フィールドのチェックだけでは不十分であり、フォーム全体の状態を検証する必要があります。

2. フォーム全体の状態管理


Reactでフォーム全体をバリデーションする際、状態管理が重要です。以下のようにuseStateまたはuseReducerを活用します。

import React, { useState } from 'react';

const validateForm = (formData) => {
  const errors = {};
  if (!formData.startDate || !formData.endDate) {
    errors.date = '開始日と終了日は必須です';
  } else if (new Date(formData.startDate) > new Date(formData.endDate)) {
    errors.date = '終了日は開始日より後である必要があります';
  }
  if (formData.total > 100) {
    errors.total = '合計値は100以下である必要があります';
  }
  return errors;
};

const FormValidationExample = () => {
  const [formData, setFormData] = useState({
    startDate: '',
    endDate: '',
    total: '',
  });
  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const validationErrors = validateForm(formData);
    setErrors(validationErrors);

    if (Object.keys(validationErrors).length === 0) {
      alert('フォームが正常に送信されました');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>開始日</label>
        <input
          type="date"
          name="startDate"
          value={formData.startDate}
          onChange={handleChange}
        />
      </div>
      <div>
        <label>終了日</label>
        <input
          type="date"
          name="endDate"
          value={formData.endDate}
          onChange={handleChange}
        />
        {errors.date && <p style={{ color: 'red' }}>{errors.date}</p>}
      </div>
      <div>
        <label>合計値</label>
        <input
          type="number"
          name="total"
          value={formData.total}
          onChange={handleChange}
        />
        {errors.total && <p style={{ color: 'red' }}>{errors.total}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default FormValidationExample;

3. フォーム全体の検証ロジック


上記の例では、validateForm関数を使用してフォーム全体の状態をチェックしています。この関数は、必要に応じて以下を検証します。

  • フィールド間の依存関係。
  • 複数フィールドの値を組み合わせた条件。
  • すべてのエラーメッセージを一括管理。

4. React Hook Formを使った効率化


フォーム全体のバリデーションを簡略化するには、React Hook Formを活用する方法もあります。以下はReact Hook Formの例です。

import React from 'react';
import { useForm } from 'react-hook-form';

const FormValidationWithHook = () => {
  const { register, handleSubmit, watch, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    alert('フォーム送信成功: ' + JSON.stringify(data));
  };

  const startDate = watch('startDate');
  const endDate = watch('endDate');

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>開始日</label>
        <input
          type="date"
          {...register('startDate', { required: '開始日は必須です' })}
        />
      </div>
      <div>
        <label>終了日</label>
        <input
          type="date"
          {...register('endDate', {
            required: '終了日は必須です',
            validate: (value) =>
              !startDate || new Date(value) >= new Date(startDate) || '終了日は開始日より後である必要があります',
          })}
        />
        {errors.endDate && <p style={{ color: 'red' }}>{errors.endDate.message}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default FormValidationWithHook;

5. フォーム全体のバリデーションの注意点

  • エラーメッセージの一貫性: ユーザーにわかりやすく、統一されたエラーメッセージを提供します。
  • パフォーマンス: 複雑なバリデーションでは計算コストを考慮します。

これらの実装により、フォーム全体のバリデーションが効率的に行えるようになります。次に、バリデーションをさらに効率化するためのReact Hook Formの応用例について解説します。

React Hook Formを使ったバリデーション強化

React Hook Formは、フォームの状態管理とバリデーションを効率的に行うための人気ライブラリです。軽量で柔軟性が高く、特にカスタムバリデーションロジックの実装を簡単にします。このセクションでは、React Hook Formを活用してフォームバリデーションを強化する方法を解説します。

1. React Hook Formの基本構造


React Hook Formを利用するには、以下の基本的なセットアップが必要です。

import React from 'react';
import { useForm } from 'react-hook-form';

const BasicForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>名前</label>
        <input
          {...register('name', { required: '名前は必須です' })}
        />
        {errors.name && <p style={{ color: 'red' }}>{errors.name.message}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default BasicForm;

このコードでは、registerを使ってフォームフィールドを登録し、handleSubmitでフォーム送信時にバリデーションを実行します。

2. カスタムバリデーションの実装


React Hook Formでは、カスタムバリデーション関数をvalidateプロパティとして定義できます。

const validateEmail = (email) =>
  /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) || '無効なメールアドレスです';

const CustomValidationForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>メールアドレス</label>
        <input
          {...register('email', {
            required: 'メールアドレスは必須です',
            validate: validateEmail,
          })}
        />
        {errors.email && <p style={{ color: 'red' }}>{errors.email.message}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default CustomValidationForm;

この例では、validateEmail関数を使い、カスタムバリデーションロジックを適用しています。

3. 動的なバリデーションロジック


React Hook Formは、動的なバリデーションにも対応しています。たとえば、別のフィールドの値に応じたバリデーションを行う場合、watchを使用します。

const DependentValidationForm = () => {
  const { register, handleSubmit, watch, formState: { errors } } = useForm();

  const password = watch('password');

  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>パスワード</label>
        <input
          type="password"
          {...register('password', { required: 'パスワードは必須です' })}
        />
      </div>
      <div>
        <label>パスワード確認</label>
        <input
          type="password"
          {...register('confirmPassword', {
            required: '確認用パスワードは必須です',
            validate: (value) => value === password || 'パスワードが一致しません',
          })}
        />
        {errors.confirmPassword && (
          <p style={{ color: 'red' }}>{errors.confirmPassword.message}</p>
        )}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default DependentValidationForm;

ここでは、confirmPasswordフィールドがpasswordと一致しているかを動的に検証しています。

4. バリデーションスキーマの使用(Yupの導入)


スキーマベースのバリデーションには、Yupライブラリを組み合わせることが効果的です。

import React from 'react';
import { useForm, Controller } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = yup.object().shape({
  name: yup.string().required('名前は必須です'),
  age: yup
    .number()
    .required('年齢は必須です')
    .min(18, '18歳以上である必要があります'),
});

const SchemaValidationForm = () => {
  const { register, handleSubmit, formState: { errors } } = useForm({
    resolver: yupResolver(schema),
  });

  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div>
        <label>名前</label>
        <input {...register('name')} />
        {errors.name && <p style={{ color: 'red' }}>{errors.name.message}</p>}
      </div>
      <div>
        <label>年齢</label>
        <input type="number" {...register('age')} />
        {errors.age && <p style={{ color: 'red' }}>{errors.age.message}</p>}
      </div>
      <button type="submit">送信</button>
    </form>
  );
};

export default SchemaValidationForm;

ここでは、Yupスキーマを使用してフォームのバリデーションルールを定義し、コードの見通しを良くしています。

5. React Hook Formのメリット

  • 軽量かつ柔軟性が高い。
  • パフォーマンスに優れており、大規模フォームでも高速に動作。
  • 簡単にカスタマイズ可能なバリデーション機能を提供。

React Hook Formを活用することで、複雑なバリデーションを効率的に実現し、ユーザーフレンドリーなフォームを作成できます。次に、よくある問題とそのトラブルシューティングについて解説します。

よくある問題とトラブルシューティング

Reactでカスタムバリデーションを実装する際、いくつかの問題が発生することがあります。このセクションでは、よくある問題の具体例とその解決策を解説します。

1. バリデーションロジックが機能しない


問題: カスタムバリデーション関数が適切に動作せず、正しい結果を返さない。

原因:

  • 入力値の型が予期しないものになっている。
  • バリデーション関数が適切に登録されていない。

解決策:

  • 入力値の型を確認し、必要であれば型変換を行います。
  const validateNumber = (value) => !isNaN(Number(value)) || '数値を入力してください';
  • フォームの値をデバッグし、正しいデータが渡されているか確認します。
  console.log(formData);

2. 複数のエラーメッセージが競合する


問題: 同じフィールドで複数のバリデーションが競合し、適切なエラーメッセージが表示されない。

原因:

  • バリデーションの順序が適切でない。
  • エラーの上書きが発生している。

解決策:

  • バリデーションルールを明確にし、優先順位を設定します。
  const validatePassword = (password) => {
    if (!password) return 'パスワードは必須です';
    if (password.length < 8) return 'パスワードは8文字以上必要です';
    return true;
  };

3. バリデーション結果が遅延する


問題: リアルタイムバリデーションが遅延し、入力に対してすぐに反応しない。

原因:

  • 状態更新が非同期であるため、遅延が発生。
  • バリデーションロジックが計算負荷の高い処理を行っている。

解決策:

  • 状態の変更を適切に制御し、不要な再レンダリングを防ぎます。
  const debounce = (func, delay) => {
    let timeout;
    return (...args) => {
      clearTimeout(timeout);
      timeout = setTimeout(() => func(...args), delay);
    };
  };
  const handleInputChange = debounce((value) => validate(value), 300);
  • 必要であればバリデーションを非同期処理に対応させます。

4. 動的フィールドでエラーが発生する


問題: 動的に追加されたフィールドでバリデーションが正しく適用されない。

原因:

  • 動的フィールドが初期状態で登録されていない。
  • 動的なフィールド名が一意でない。

解決策:

  • 動的フィールドを一意の名前で登録します。
  const fields = watch('dynamicFields', []);
  • React Hook Formを使用して動的フィールドを管理します。
  const { fields, append, remove } = useFieldArray({ name: 'dynamicFields' });

5. カスタムバリデーションでエラーハンドリングが不足


問題: カスタムバリデーション中に例外が発生し、エラーが処理されない。

原因:

  • バリデーション関数でエラー処理が不足している。

解決策:

  • 例外処理を明示的に追加します。
  const validateField = (value) => {
    try {
      if (!value) throw new Error('入力が必要です');
      return true;
    } catch (error) {
      return error.message;
    }
  };

6. ユーザーエクスペリエンスが低下する


問題: エラーメッセージが頻繁に表示され、ユーザーにとって煩わしい。

原因:

  • バリデーションがリアルタイムすぎる。
  • エラーメッセージが簡潔でない。

解決策:

  • バリデーションを送信時またはフィールドからフォーカスが外れた際に実行するよう変更します。
  <input onBlur={handleBlur} />
  • ユーザーが次に何をすべきかが分かるエラーメッセージを提供します。

7. デバッグのためのツール


バリデーションエラーの調査を簡単にするため、次のツールを活用します。

  • React DevTools: コンポーネントの状態とプロップスを確認。
  • React Hook Form DevTools: フォームのデバッグに特化したツール。

これらの解決策を活用すれば、カスタムバリデーションにおける問題を迅速に解消し、安定したフォームを構築できます。次に、応用例として動的フィールドのバリデーションを紹介します。

応用例: 動的フィールドのバリデーション

フォームフィールドが動的に追加される場合、それらにも適切なバリデーションを適用する必要があります。ここでは、Reactで動的フィールドにカスタムバリデーションを適用する方法を解説します。

1. 動的フィールドの利用ケース


動的フィールドは、ユーザーが必要に応じてフィールドを追加・削除できるようにする場合に使われます。具体例として以下のようなケースがあります。

  • ショッピングカート内の複数の商品入力。
  • プロジェクトタスクの追加。
  • ユーザー情報の複数入力(例: 家族メンバーの情報)。

2. 動的フィールドの基本構造


React Hook Formを使用すると、動的フィールドを簡単に管理できます。useFieldArrayフックを活用して動的な配列を管理します。

import React from 'react';
import { useForm, useFieldArray } from 'react-hook-form';

const DynamicFieldsForm = () => {
  const { register, control, handleSubmit, formState: { errors } } = useForm();
  const { fields, append, remove } = useFieldArray({ control, name: 'items' });

  const onSubmit = (data) => {
    alert(JSON.stringify(data));
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <h2>商品情報</h2>
      {fields.map((field, index) => (
        <div key={field.id}>
          <label>商品名</label>
          <input
            {...register(`items.${index}.name`, { required: '商品名は必須です' })}
          />
          {errors.items?.[index]?.name && (
            <p style={{ color: 'red' }}>{errors.items[index].name.message}</p>
          )}
          <label>数量</label>
          <input
            type="number"
            {...register(`items.${index}.quantity`, {
              required: '数量は必須です',
              min: { value: 1, message: '数量は1以上である必要があります' },
            })}
          />
          {errors.items?.[index]?.quantity && (
            <p style={{ color: 'red' }}>{errors.items[index].quantity.message}</p>
          )}
          <button type="button" onClick={() => remove(index)}>削除</button>
        </div>
      ))}
      <button type="button" onClick={() => append({ name: '', quantity: 1 })}>
        商品を追加
      </button>
      <button type="submit">送信</button>
    </form>
  );
};

export default DynamicFieldsForm;

3. コードのポイント

  • useFieldArrayの利用
    useFieldArrayを使うことで、動的に追加・削除されるフィールドを効率的に管理できます。
  • バリデーションルールの適用
    各フィールドに独自のバリデーションルールを指定できます。ここでは、商品名の必須チェックと数量の最小値チェックを行っています。

4. 動的フィールドにおけるエラー管理


動的フィールドでは、エラーが配列形式で管理されるため、以下のように確認します。

{errors.items?.[index]?.name && (
  <p style={{ color: 'red' }}>{errors.items[index].name.message}</p>
)}

このコードは、各フィールドのエラーを正確に表示するためのものです。

5. バリデーションの応用例


動的フィールドのバリデーションロジックをさらに高度化することも可能です。たとえば、合計数量の上限を設ける場合は以下のようにします。

const validateTotalQuantity = (items) => {
  const total = items.reduce((sum, item) => sum + (item.quantity || 0), 0);
  return total <= 100 || '合計数量は100以下である必要があります';
};

const { handleSubmit, register, control, watch } = useForm({
  defaultValues: { items: [{ name: '', quantity: 1 }] },
  mode: 'onChange',
  resolver: (data) => validateTotalQuantity(data.items),
});

ここでは、配列全体を対象としたカスタムバリデーションを行っています。

6. ユーザーエクスペリエンスの向上


動的フィールドのバリデーションは、次のような工夫でさらに使いやすくなります。

  • リアルタイムバリデーション: 入力のたびにエラーが即時表示されるようにする。
  • フィールドの初期化: 新しいフィールドを追加する際、デフォルト値を設定する。
  • エラーハイライト: エラーが発生しているフィールドを視覚的に強調する。

これにより、動的フォームでのユーザーフレンドリーな体験を提供できます。次に、記事全体をまとめます。

まとめ

本記事では、Reactでカスタムバリデーションロジックを実装する方法をステップバイステップで解説しました。基本的なフィールド単位のバリデーションから、フォーム全体の状態を考慮した複雑なバリデーション、さらには動的フィールドへの応用までを網羅しました。React Hook Formを活用することで、効率的かつ柔軟にバリデーションロジックを構築できることも示しました。

適切なバリデーションは、フォームの信頼性を向上させるだけでなく、ユーザーフレンドリーな体験を提供します。実装にあたっては、エラーの一貫性やパフォーマンスへの配慮も重要です。この記事の内容を参考に、より洗練されたフォームバリデーションを実現してください。

コメント

コメントする

目次
  1. カスタムバリデーションロジックの概要
    1. カスタムバリデーションの主な目的
    2. 利用ケース
  2. React標準バリデーションとの違い
    1. 標準バリデーションの特徴
    2. カスタムバリデーションの特徴
    3. 具体例での違い
    4. 使い分けのポイント
  3. カスタムバリデーションの実装準備
    1. 1. 開発環境の設定
    2. 2. 必要なライブラリのインストール
    3. 3. プロジェクト構成の確認
    4. 4. バリデーションロジックの基本設計
    5. 5. 状態管理の準備
  4. フィールド単位のバリデーションロジック作成
    1. 1. フィールドバリデーションの基本構造
    2. 2. Reactコンポーネントでの利用
    3. 3. リアルタイムバリデーションの実装
    4. 4. 複数条件のバリデーション
    5. 5. フィールドバリデーションのモジュール化
  5. フォーム全体のバリデーション
    1. 1. フォーム全体のバリデーションが必要なケース
    2. 2. フォーム全体の状態管理
    3. 3. フォーム全体の検証ロジック
    4. 4. React Hook Formを使った効率化
    5. 5. フォーム全体のバリデーションの注意点
  6. React Hook Formを使ったバリデーション強化
    1. 1. React Hook Formの基本構造
    2. 2. カスタムバリデーションの実装
    3. 3. 動的なバリデーションロジック
    4. 4. バリデーションスキーマの使用(Yupの導入)
    5. 5. React Hook Formのメリット
  7. よくある問題とトラブルシューティング
    1. 1. バリデーションロジックが機能しない
    2. 2. 複数のエラーメッセージが競合する
    3. 3. バリデーション結果が遅延する
    4. 4. 動的フィールドでエラーが発生する
    5. 5. カスタムバリデーションでエラーハンドリングが不足
    6. 6. ユーザーエクスペリエンスが低下する
    7. 7. デバッグのためのツール
  8. 応用例: 動的フィールドのバリデーション
    1. 1. 動的フィールドの利用ケース
    2. 2. 動的フィールドの基本構造
    3. 3. コードのポイント
    4. 4. 動的フィールドにおけるエラー管理
    5. 5. バリデーションの応用例
    6. 6. ユーザーエクスペリエンスの向上
  9. まとめ