0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

react yup で 片方入力したら両方必須にしたい

Posted at

Yupで2つの入力項目を相互監視する実装方法

フォームのバリデーションで「2つの入力項目のどちらかが入力されたら、もう片方も必須にする」という要件は、よく遭遇するパターンです。本記事では、YupとReact Hook Formを使用してこの要件を実装する方法を解説します。

実装の課題

Yupは基本的に入力中の入力欄に対してのバリデーションを実行しますが、2つの入力項目を相互に監視する場合、以下のような課題があります:

  1. 一方の入力項目が変更された時、もう片方の入力項目のバリデーションが即座に実行されない
  2. 初期値がnullの場合にバリデーションエラーが発生する
  3. バリデーションの循環参照による無限ループの可能性

解決方法

1. スキーマの定義

type FormValues = {
  optionalField1?: string | null;
  optionalField2?: string | null;
};

const schema = yup.object().shape(
  {
    optionalField1: yup.string()
      .nullable() // null許容
      .when('optionalField2', (optionalField2, schema) => {
        if(optionalField2) return schema.required('両方の項目を入力してください');
        return schema;
      }),
    optionalField2: yup.string()
      .nullable() // null許容
      .when('optionalField1', (optionalField1, schema) => {
        if(optionalField1) return schema.required('両方の項目を入力してください');
        return schema;
      }),
  },
  [['optionalField1', 'optionalField2']] // 循環参照の解決
);

2. フォームコンポーネントの実装

const Form = () => {
  const {
    register,
    watch,
    trigger,
    formState: { errors }
  } = useForm<FormValues>({
    resolver: yupResolver(schema),
    mode: 'onChange'
  });

  // 相互監視の実装
  useEffect(() => {
    trigger(['optionalField1', 'optionalField2']);
  }, [trigger, watch('optionalField1'), watch('optionalField2')]);

  return (
    <form>
      <input {...register('optionalField1')} />
      {errors.optionalField1 && <p>{errors.optionalField1.message}</p>}

      <input {...register('optionalField2')} />
      {errors.optionalField2 && <p>{errors.optionalField2.message}</p>}
    </form>
  );
};

実装のポイント

1. nullable()の使用

optionalField1: yup.string().nullable()
  • 初期値がnullの場合でもバリデーションエラーが発生しない
  • オプショナルな入力項目の場合は基本的に付けることを推奨

2. useEffectによる監視

useEffect(() => {
  trigger(['optionalField1', 'optionalField2']);
}, [trigger, watch('optionalField1'), watch('optionalField2')]);
  • watchで両フィールドの値を監視
  • 値の変更時にtriggerで関連フィールドのバリデーションを強制実行
  • これにより相互の入力状態を適切に監視可能

3. 循環参照の解決

[['optionalField1', 'optionalField2']]
  • スキーマ定義の第2引数に相互参照するフィールドの配列を指定
  • これによりバリデーションの無限ループを防止

まとめ

この実装により、以下のような利点が得られます:

  1. シンプルで保守性の高いコード
  2. 適切な相互バリデーション
  3. 初期値のnull対応
  4. 無限ループの防止

特にnullable()の使用とuseEffectでのtriggerの組み合わせがポイントです。これにより、複雑なフォームのバリデーション要件にも対応できます。

参考リンク

0
0
1

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?