🦀

yup¦日付の相関チェックなど少し込み入ったバリデーションをする

2024/08/02に公開

はじめに

yupで少し複雑なバリデーションをしたいときにやり方を調べるのにやや苦戦したのでそのまとめになります。

やりたいこと

  1. ふたつの入力値を比較するバリデーション
    • 開始日と終了日の入力欄を比較する
  2. 入力可能な文字が決まっているバリデーション
    • 任意入力項目
  3. 配列で選択肢から選択しているかを確認するバリデーション

環境

ライブラリなど バージョン
React 18.2.0
TypeScript 5.0.4
yup 1.0.2
date-fns 2.29.3

実装

ふたつの入力値を比較するバリデーション

開始日と終了日の入力欄を比較するバリデーションの実装です。下記を満たすバリデーションになります。

  • 開始日と終了日は必須入力項目
  • 開始日と終了日が同一の日付はOK
  • 開始日が終了日後の日付はNG
const validationSchema = Yup.object().shape({
  start_date: Yup.date()
    .typeError('正しい日付を入力してください。')
    .required('開始日は必ず入力してください。')
    .test(
      'custom_date_validation',
      '開始日は終了日以前の日付を選択してください。',
      function (start) {
        if (isSameDay(start, this.parent.end_date)) {
          return true
        }
        return isBefore(start, this.parent.end_date)
      },
    ),
  end_date: Yup.date()
    .typeError('正しい日付を入力してください。')
    .required('終了日は必ず入力してください。')
    .test(
      'custom_date_validation',
      '',
      function (end) {
        if (isSameDay(this.parent.start_date, end)) {
          return true
        }
        return isBefore(this.parent.start_date, end)
      },
    })
});

isSameDayisBeforedate-fnsを使用しています。
開始日、終了日両方にカスタムバリデーションをつけたのは、片方だけにバリデーションをつけた場合に片方の入力欄のみエラー表示になってしまうため両方にカスタムバリデーションをつけました。

入力可能な文字が決まっているバリデーション

  • 入力欄は文字が入力される
  • 任意入力項目
  • 入力可能な文字の一覧と合致すればOK

MUIのAutocomplete(選択肢から入力可能だが入力欄を直接入力・編集できるタイプの入力欄)に対してのバリデーションで実装しました。

直接入力・編集はできるのですが、選択肢以外を選択できないようにして欲しいとのことでした。
※MUIのコンポーネントで直接入力・編集不可にすることはできるのですが、あくまで直接入力・編集はできるがバリデーションでNGにしてほしいとのことでした。もはやセレクトボックス

const validationSchema = Yup.object().shape({
  text: Yup.string()
    .nullable()
    .transform((text) => {
      return text === '' ? null : text
    })
    .oneOf(options, '入力候補から選択してください。')
});

optionsは選択可能な文字の配列です。
現状では任意入力項目だけど、入力された場合はバリデーションする場合、transformを使用し未入力の場合はnullを返すようにしないとエラーになってしまうようでした。

配列で選択肢から選択しているかを確認するバリデーション

  • 必須入力項目
  • 選択肢と合致しているかを確認する

チェックボックスなど複数の選択肢から複数選択可能な入力欄の場合に一応選択肢と合致しているかを確認するバリデーションです。

const validationSchema = Yup.object().shape({
  items: Yup.array()
    .test('custom_radio_validation', '選択が必須の項目です。', (items) => {
      if (!items || items.length === 0) return false
      return items.every((item) => optioins.includes(item))
    })
});

optionsは選択可能な選択肢の配列です。
複数選択可能なため、配列と配列を比較する必要があるため、everyとincludesを使用しています。


個人的な印象ではありますが
zodに関しては調べたらすぐ解決することが多いのですが、yupは個人的になかなか苦戦しました。。。

少し観点がずれますが、エラーメッセージを毎回個別に書くのがめんどくさい気もするのですが、まとめて管理することも可能なのでその点はとても便利だな〜と思いました(* 'ᵕ' )

Discussion