- Published on
Deep Validation in Array of Objects with Yup and Formik
- Authors
- Name
- Ripal & Zalak
Deep Validation in Array of Objects with Yup and Formik
When working with complex forms in React, especially those containing arrays of objects, you may encounter scenarios where you need to validate that at least one item in an array meets specific criteria. In this blog, we’ll explore how to achieve this using Yup with Formik.
The Problem
Imagine a form structure like this:
{
"subject": "Task Title",
"description": "Task Description",
"daysOfWeek": [
{ "dayOfWeek": "MONDAY", "checked": false },
{ "dayOfWeek": "TUESDAY", "checked": false },
{ "dayOfWeek": "WEDNESDAY", "checked": true },
{ "dayOfWeek": "THURSDAY", "checked": false },
{ "dayOfWeek": "FRIDAY", "checked": false }
],
"uuid": "unique-id"
}
You want to ensure at least one daysOfWeek object has checked: true.
Solution with Yup
To validate this, we use Yup’s .test method. Here's the validation schema:
import * as Yup from 'yup'
const validationSchema = Yup.object().shape({
subject: Yup.string().required('Subject is required'),
description: Yup.string(),
daysOfWeek: Yup.array()
.of(
Yup.object().shape({
dayOfWeek: Yup.string().required('Day is required'),
checked: Yup.boolean(),
})
)
.test('at-least-one-checked', 'At least one day must be checked', (days) => {
return days.some((day) => day.checked)
}),
uuid: Yup.string().required('UUID is required'),
})
Explanation
- Schema Definition:
daysOfWeekis an array where each item must adhere to its own schema.- The
.testmethod ensures at least onecheckedfield istrue.
Form Implementation with Formik
Here’s how you can integrate this schema with a Formik form:
import React from 'react'
import { Formik, Field, Form } from 'formik'
import * as Yup from 'yup'
const initialValues = {
subject: '',
description: '',
daysOfWeek: [
{ dayOfWeek: 'MONDAY', checked: false },
{ dayOfWeek: 'TUESDAY', checked: false },
{ dayOfWeek: 'WEDNESDAY', checked: false },
],
uuid: '',
}
const validationSchema = Yup.object().shape({
// (Use the schema defined above)
})
const MyForm = () => {
return (
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => {
console.log('Form Submitted:', values)
}}
>
{({ values, errors, touched }) => (
<Form>
<div>
<label>Subject:</label>
<Field name="subject" type="text" />
{errors.subject && touched.subject && <div>{errors.subject}</div>}
</div>
<div>
<label>Description:</label>
<Field name="description" type="text" />
</div>
<div>
<label>Days of Week:</label>
{values.daysOfWeek.map((day, index) => (
<div key={index}>
<Field name={`daysOfWeek[${index}].checked`} type="checkbox" />
{day.dayOfWeek}
</div>
))}
{errors.daysOfWeek && <div>{errors.daysOfWeek}</div>}
</div>
<div>
<label>UUID:</label>
<Field name="uuid" type="text" />
{errors.uuid && touched.uuid && <div>{errors.uuid}</div>}
</div>
<button type="submit">Submit</button>
</Form>
)}
</Formik>
)
}
export default MyForm
FAQs
1. Why use .test instead of .min?
.min validates the array length, but .test allows custom logic, such as ensuring one checked field is true.
2. Can this be extended to multiple conditions?
Yes! Modify the .test function to include additional conditions as needed.
3. What if no days are selected?
The error message defined in .test will trigger, ensuring proper user feedback.
