DEV Community

Cover image for Yup.array validate object property is unique with Formik in React.
Anlisha Maharjan
Anlisha Maharjan

Posted on • Originally published at anlisha.com.np on

1 1

Yup.array validate object property is unique with Formik in React.

The requirement is to validate objects in users array has unique name property.

Define a custom method in Yup using Yup.addMethod. Yup.js provides this function to conveniently reuse test functions. Function has to either return true or false to indicate if testing has failed or not respectively, or a new ValidationError created through this.createError().

Yup.addMethod(Yup.array, "unique", function (message, path) {
  return this.test("unique", message, function (list) {
    const mapper = (x) => get(x, path);
    const set = [...new Set(list.map(mapper))];
    const isUnique = list.length === set.length;
    if (isUnique) {
      return true;
    }
    const idx = list.findIndex((l, i) => mapper(l) !== set[i]);
    return this.createError({path: `users[${idx}].${path}`,message});
 });
});
Enter fullscreen mode Exit fullscreen mode

With Yup, we create a Yup formatted object that resembles our intended schema for an object.

const schema = Yup.object().shape({
 users: Yup.array().of(
   Yup.object().shape({
     name: Yup.string().required("Name is required"),        
   })).unique("Duplicate user", "name")
});
Enter fullscreen mode Exit fullscreen mode

Formik has a special config option / prop for Yup object schemas called validationSchema which will automatically transform Yup’s validation errors into a pretty object whose keys match values and touched. Set the validationSchema option to the schema created above.

Use FieldArray that helps with common array/list manipulations. Pass it a name property name=”users”. FieldArray will give access to array helper methods via render props.

import React from "react";
import * as Mui from "@mui/material";
import { Formik, Form, Field, FieldArray } from "formik";
import * as Yup from "yup";
import { get } from "lodash";
const YupValidation = () => {
Yup.addMethod(Yup.array, "unique", function (message, path) {
return this.test("unique", message, function (list) {
const mapper = (x) => get(x, path);
const set = [...new Set(list.map(mapper))];
const isUnique = list.length === set.length;
if (isUnique) {
return true;
}
const idx = list.findIndex((l, i) => mapper(l) !== set[i]);
return this.createError({ path: `users[${idx}].${path}`, message });
});
});
const schema = Yup.object().shape({
users: Yup.array()
.of(
Yup.object().shape({
name: Yup.string().required("Name is required"),
})
)
.unique("Duplicate user", "name"),
});
return (
<Formik
enableReinitialize={true}
initialValues={{
users: [{ name: "" }],
}}
validationSchema={schema}
onSubmit={(values) => console.log(values)}
>
{({ values, errors, touched }) => (
<Form>
<FieldArray
name="users"
render={(arrayHelpers) => (
<>
<Mui.Box display="flex" justifyContent="space-between" alignItems="center">
<Mui.Typography variant="h5" component="h5">
Users
</Mui.Typography>
<Mui.Button
variant="contained"
color="primary"
disableElevation
type="button"
onClick={() => {
arrayHelpers.push({
name: "",
});
}}
>
Add New
</Mui.Button>
</Mui.Box>
<Mui.TableContainer>
<Mui.Table>
<Mui.TableHead>
<Mui.TableRow>
<Mui.TableCell component="th">SN</Mui.TableCell>
<Mui.TableCell component="th">USER</Mui.TableCell>
<Mui.TableCell component="th"></Mui.TableCell>
</Mui.TableRow>
</Mui.TableHead>
<Mui.TableBody>
{values.users.map((item, index) => (
<Mui.TableRow key={index}>
<Mui.TableCell component="td">{index + 1}</Mui.TableCell>
<Mui.TableCell component="td">
<Field name={`users.${index}.name`}>
{({ field, meta }) => (
<Mui.TextField
InputLabelProps={{ shrink: true }}
variant="outlined"
placeholder="Name"
helperText={
touched?.users?.[index]?.name && errors?.users?.[index]?.name
? errors?.users?.[index]?.name
: null
}
error={errors?.users?.[index]?.name ? true : false}
{...field}
/>
)}
</Field>
</Mui.TableCell>
<Mui.TableCell component="td">
<Mui.IconButton
size="small"
className="svg-danger"
onClick={() => arrayHelpers.remove(index)}
>
X
</Mui.IconButton>
</Mui.TableCell>
</Mui.TableRow>
))}
</Mui.TableBody>
</Mui.Table>
</Mui.TableContainer>
</>
)}
/>
<Mui.Box mt={2} width="100%" display="flex" justifyContent="flex-end" flexWrap="wrap">
<Mui.Button variant="contained" color="primary" disableElevation type="submit">
Submit
</Mui.Button>
</Mui.Box>
</Form>
)}
</Formik>
);
};
export default YupValidation;

Note on line 57, arrayHelpers.push add a value to the end of an array. Similarly, arrayHelpers.remove on line 104 remove an element at an index of an array and return it.

Field hooks up inputs to Formik. Note the name attribute on line 82; it uses to match up with Formik state.

Thank you for reading!

The post Yup.array validate object property is unique with Formik in React. first appeared on Anlisha Maharjan.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more