Yupで2つの入力項目を相互監視する実装方法
フォームのバリデーションで「2つの入力項目のどちらかが入力されたら、もう片方も必須にする」という要件は、よく遭遇するパターンです。本記事では、YupとReact Hook Formを使用してこの要件を実装する方法を解説します。
実装の課題
Yupは基本的に入力中の入力欄に対してのバリデーションを実行しますが、2つの入力項目を相互に監視する場合、以下のような課題があります:
- 一方の入力項目が変更された時、もう片方の入力項目のバリデーションが即座に実行されない
- 初期値が
null
の場合にバリデーションエラーが発生する - バリデーションの循環参照による無限ループの可能性
解決方法
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引数に相互参照するフィールドの配列を指定
- これによりバリデーションの無限ループを防止
まとめ
この実装により、以下のような利点が得られます:
- シンプルで保守性の高いコード
- 適切な相互バリデーション
- 初期値の
null
対応 - 無限ループの防止
特にnullable()
の使用とuseEffect
でのtrigger
の組み合わせがポイントです。これにより、複雑なフォームのバリデーション要件にも対応できます。