Hey guys, let's get straight to the point, I'll show you how to Yup validate two fields that depend on each other.
You may have faced this problem already. If not, you'll face it.
Here is an example of what will happen:
const yup = require('yup')
const {
setLocale
} = yup
setLocale({
mixed: {
notType: 'the ${path} is obligatory',
required: 'the field ${path} is obligatory',
oneOf: 'the field ${path} must have one of the following values: ${values}'
}
})
const myNameSchema = yup.object().shape({
first_name: yup.string().ensure().when('surname', {
is: '',
then: yup.string().required()
}),
surname: yup.string().ensure().when('first_name', {
is: '',
then: yup.string().required()
})
})
[...]
Error: Cyclic dependency, node was:"surname"
at visit (/home/{yourPath}/node_modules/toposort/index.js:45:13)
at visit (/home/{yourPath}/node_modules/toposort/index.js:62:9)
at visit (/home/{yourPath}/node_modules/toposort/index.js:62:9)
at Function.toposort [as array]...
Cyclic error, but how are we going to solve this?
Fields that depend on each other to be validated need to sorted so they are "constructed" in the correct order, e.g. if depend on field A
in field B
, you needs to cast and coerce the value in field A
before it's handed to B
.
What is happening here is that we are just adding a validation to the condition, so there is really no need to request anything for the validation to happen after everything is constructed already. Due to the flexibility and programmatic nature of Yup it can't distinguish between those two cases.
Solution:
const yup = require('yup')
const {
setLocale
} = yup
setLocale({
mixed: {
notType: 'the ${path} is obligatory',
required: 'the field ${path} is obligatory',
oneOf: 'the field ${path} must have one of the following values: ${values}'
}
})
const myNameSchema = yup.object().shape({
first_name: yup.string().ensure().when('surname', {
is: '',
then: yup.string().required()
}),
surname: yup.string().ensure().when('first_name', {
is: '',
then: yup.string().required()
})
}, [['surname', 'first_name']]) // <--- adding your fields which need validation
[...]
{
"message": "there was an error validating data",
"error": [
"the field first_name is obligatory",
"the field surname is obligatory"
]
}
Another example with extra fields:
[...]
const myNameSchema = yup.object().shape({
first_name: yup.string().when(['surname', 'age'], {
is: (surname, age) => !surname && !age,
then: yup.string().required()
}),
surname: yup.string().when(['first_name', 'age'], {
is: (first_name, age) => !first_name && !age,
then: yup.string().required()
}),
age: yup.number().when(['first_name', 'surname'], {
is: (first_name, surname) => !first_name && !surname,
then: yup.number().required()
})
}, [
['first_name', 'surname'], // <--- adding your fields which need validation
['first_name', 'age'],
['surname', 'age']
])
[...]
{
"message": "there was an error validating data",
"error": [
"the field first_name is obligatory",
"the field surname is obligatory",
"the field age is obligatory"
]
}
I hope I helped you! 😉
Thank you!!
My GitHub 😎
Top comments (7)
Thanks a ton!!!
thank you so much. I literally spent 3 hours trying to find a solution for this stupid error. finally this solved it. THANK YOU!
Thanks and it truly worked. Could you please share more about [['field_name']] sytax? I checked the doc but still not get the point.
Thanks! 🙏
I was working on this for hours.
Thanks a ton!!!!! 😱🥷
Wow it look good, but i cannot get it at
is: (surname, age) => !surname && !age
what does it mean? thanks
Its checking that when surname and age is a falsy value (false, empty string, undefined, 0, etc), use the
then: yup.string().required()
.