ほぼ1日かけて探したけどわからないので供養
const uniqueId = (value, context) => { const [_, parent] = context.from const filedNames = ['hogehoge1', 'hogehoge2', 'hogehoge3'] const list = [ parent.value.hogehoge1.id, parent.value.hogehoge2.id, parent.value.hogehoge3.id, ] console.log(list) const r = !list || list.length === new Set(list).size if (r) return true const errors = [] const duplicateErrorMessage = 'IDは重複してるので入力できません。' // とりあえず雑に組む(ry const a1 = list[0] === list[1] const a2 = list[0] === list[2] const a3 = list[1] === list[2] if (a1) { errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[0]}.id`)) errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[1]}.id`)) } if (a2) { if (!a1) { errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[0]}.id`)) } errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[2]}.id`)) } if (a3) { if (!a1) { errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[1]}.id`)) } if (!a2) { errors.push(new yup.ValidationError(duplicateErrorMessage, 'type', `${filedNames[2]}.id`)) } } return new yup.ValidationError(errors) } const defaultSchema = { hogehoge1: yup.object({ id: yup.string().required('IDを入力してください') // TODO: 他のidと重複しちゃだめ .test('unique_id', uniqueId), location: yup.string(), }), hogehoge2: yup.object({ id: yup.string().required('IDを入力してください') // TODO: 他のidと重複しちゃだめ .test('unique_id', uniqueId), location: yup.string(), }), hogehoge3: yup.object({ id: yup.string().required('IDを入力してください') // TODO: 他のidと重複しちゃだめ .test('unique_id', uniqueId), }), } const schema = yup.object(defaultSchema) // 省略 const { register, handleSubmit, formState: { errors }, watch, control, } = useForm<CreateInput>({ resolver: yupResolver(schema), })
やりたかったこと
- 1 特定の複数フィールドのidが重複していないかバリデーションをかけたい
- 2 a,b,cとあった場合、a,bのフィールドが重複している場合、a,bのフィールドにバリデーションエラーメッセージを表示したい
- 3 a,bのフィールドが重複してエラーメッセージが表示されている場合、aを修正するとa,bそれぞれのフィールドからバリデーションエラーメッセージが非表示になる
できなかったこと
- 1,2はできた、3ができなかった
- aを治すとaは非表示になるが、bはバリデーションが動かないっぽいので表示されたままになっている
できなかったことについて試したこと・探したこと
- ValidationErrorを返したらエラーが表示されるのでreturnしたらクリアされるやつとかないかな?
- なさそう
- Yupを外部から無理やりでもいいからフィールド指定したらバリデーション叩いてくれるやつないかな
- なさそう(引数食わしたらバリデーション実行できるやつはあるけど、状態が変化するやつじゃないのでFormまで伝播(?)はしない
- react-hook-formのtriggerやFormStateを駆使してyupのバリデーションを叩いたり、errorsを書き換えようとした
- Yupの世界からでてしまうので嫌だなと思ったのと、バリデーション実行するとレンダリングが複数走るので実装が複雑になっちゃうのと、Yup → react-hook-formがYup ↔ react-hook-form の依存になるのでこれも嫌だなと思ったため諦めた
学んだこと
- schemaをネストする
- ↑のように
hogehoge1_id
でもできるけど、ネストしてhogehoge1.id
でもできることがわかった - ただその場合、 ネストしたobjectを
yup.object()
にわたす必要がある。じゃないとエラーになる
- ↑のように
- ValidationErrorで返すと複数エラーメッセージを返せる
- CreateErrorで返すサンプルが多かったんですが、pathを複数もたせる(複数フィールドをバリデーションエラーにしたい)場合どうすれば?をぐぐると、ValidationErrorを配列に詰めて最後にValidationErrorに食わせて返すでおk
- フィールドに紐付けず、schemaから
.test
メソッドなどを生やしてバリデーションを書くことができる- 全体の値や一部フィールドの値を使ってバリデーションを実行するってこともできることがわかった
(追記)実装パターン追加
いずれも解決できず。。。orz
refを使う
他フィールドを参照して自分フィールドの値と同じかどうか確認する
hogehoge1_id: yup.string().required('hogehoge1 IDを入力してください') .notOneOf([yup.ref('hogehoge2_id')], 'hogehoge2_idと同じ値です') .notOneOf([yup.ref('hogehoge3_id')], 'hogehoge3_idと同じ値です')
ただ、hogehoge2_idが重複しててもエラーメッセージは hogehoge3_idと同じ値です
になる。なぜか上書きされる
whenを使う
whenは1つしか引数与えられないと思いきや複数行けた。良い!
hogehoge1_1: yup.string() .when(['hogehoge2_id', 'hogehoge3_id'], (hogehoge2_id, hogehoge3_id, schema) => { return schema.notOneOf([hogehoge2_id, hogehoge2_id], 'hogehoge2 ID、またはhogehoge3 IDは同じ値で入力できません。') }) .required('hogehoge1 Device IDを入力してください'),
TypeScriptだと下記エラーがでる。わからないのでほっといている
型 '(hogehoge2_id: any, hogehoge3_id: any, schema: any) => any' の引数を型 'ConditionOptions<StringSchema<string | undefined, AnyObject, string | undefined>>' のパラメーターに割り当てることはできません。 型 '(hogehoge2_id: any, hogehoge3_id: any, schema: any) => any' を型 'ConditionBuilder<StringSchema<string | undefined, AnyObject, string | undefined>>' に割り当てることはできません。ts(2345)
で?結局できたの?
できませんでした。 実装はスッキリさすことができたが、 できなかったこと
をクリアすることができなかった。