- 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:
daysOfWeek
is an array where each item must adhere to its own schema.- The
.test
method ensures at least onechecked
field 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
.test
instead of .min
?
1. Why use .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.