I’ve created a multi-step form in “react”: “^17.0.1”, “yup”: “^0.29.3”, and “formik”: “^2.2.3”.
I want to check that when a user enters in a their Birth Day (dobD) that it is valid based on Birth Month (dobM) and Birth Year (dobY).
I have 3 separate inputs. dobM, dobD, dobY
The first check for dobD works (i.e the user can only enter a value between 1 and 31) however it doesn’t validate correctly if it a month with less than 31 days (for example June or September) OR if the month is February (which only 28 days except for leap years).
I tried using Yup.ref to reference the year and month fields inside of the day field validation, however if I type for the month 04, the user can still enter 31 which is incorrect (since April (04) only has 30 days).
Any ideas how can I fix this? Thank you!
Here is the validation in Yup I’m currently using:
// Step 3: Date of Birth Yup.object().shape({ dobM: Yup.string() .test( 'dobM', 'Invalid Month', value => { if (value < 1 || value > 12) { return false; } return true; } ) .min(2, 'Invalid') .max(2, 'Invalid') .required('Required'), dobY: Yup.string() .test( 'dobY', 'Valid Year required', value => { const today = new Date(); const adultYear = today.getFullYear() - 17; if (value < 1900 || value > adultYear) { return false; } return true; } ) .min(4, 'Must be 4 digits') .max(4, 'Must be 4 digits') .required('Valid Year required'), dobD: Yup.string() .test( 'dobD', 'Invalid Day', value => { if (value < 1 || value > 31) { return false; } // Check months with less than 31 days - DOESNT WORK // 4. April // 6. June // 9. September // 11. November if ((Yup.ref('dobM') == 4 || Yup.ref('dobM') == 6 || Yup.ref('dobM') == 9 || Yup.ref('dobM') == 11) && value == 31) { return false; } // If February - DOESNT WORK if (Yup.ref('dobM') == 2) { const isLeapYear = Yup.ref('dobY') % 4 == 0 && (Yup.ref('dobY') % 100 != 0 || Yup.ref('dobY') % 400 == 0); if (day > 29 || (day == 29 && !isLeapYear)) { return false; } } return true; } ) .min(2, 'Invalid') .max(2, 'Invalid') .required('Required'), }),
Advertisement
Answer
Posting my solution in hopes that this helps someone else.
I was using Yup.ref incorrectly (Yup.ref(‘fieldname’) is an object, not a single value).
** To be able to access another field in Yup I converted from an arrow function to a regular function in my test, and then could access the field values using
this.options.parent.FIELD_NAME
seen in this example:
function(day) { const month = this.options.parent.dobM; const year = this.options.parent.dobY; // check whatever you want with the value of month and year }
Full DOB validation:
// Step 3: Date of Birth Yup.object().shape({ dobM: Yup.string() .matches(/^(0[1-9]|1[012])$/, 'Invalid Month') .test( 'dobM', 'Invalid Month', value => { if (value < 1 || value > 12) { return false; } return true; } ) .min(2, 'Invalid') .max(2, 'Invalid') .required('Required'), dobY: Yup.string() .test( 'dobY', 'Valid Year required', value => { const today = new Date(); const adultYear = today.getFullYear() - 17; if (value < 1900 || value > adultYear) { return false; } return true; } ) .matches(/^[0-9]+$/, 'Must be only digits') .min(4, 'Must be 4 digits') .max(4, 'Must be 4 digits') .required('Valid Year required'), dobD: Yup.string() .test( 'dobD', 'Invalid Day', function(day) { const month = this.options.parent.dobM; const year = this.options.parent.dobY; // February if (month == 2) { const isLeapYear = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); if (day > 29 || (day == 29 && !isLeapYear)) { return false; } } return true; } ) .test( 'dobD', 'Invalid Day', function(day) { const month = this.options.parent.dobM; // Check months with less than 31 days // 4. April // 6. June // 9. September // 11. November if ((month == 4 || month == 6 || month == 9 || month == 11) && day == 31) { return false; } return true; } ) .test( 'dobD', 'Invalid Day', day => { if (day < 1 || day > 31) { return false; } return true; } ) .matches(/^[0-9]+$/, 'Digits Only') .min(2, 'Invalid Day') .max(2, 'Invalid Day') .required('Required'), }),
Sidenote: For readability, I moved each check for dobD into it’s own Yup .test(), however it’s not required.