Search code examples
reactjsmaterial-uiformikemail-validationformik-material-ui

How to validate multiple emails using one validation schema field in Formik?


I have a customer form which takes two values.

  1. Customer name
  2. Customer Emails (Which can be multiples)

I've given an add button besides the email field through which user can add more emails to the form. now i want to validate each email that's been added. also if it's added it is required too. empty email is not allowed.

The question is i have only one validation schema to validate email field. how can i use the same field to validate multiple emails?

Even after adding the correct email. it still gives error !

Please see the sandbox link to see the code. code sandbox link


Solution

  • Here is the working code with multiple email validation and errors.
    I've used Formik FieldArray to handle multiple emails.
    You can replace your code with this in your sandbox to test.

    import React from "react";
    import { TextField, IconButton } from "@material-ui/core";
    import {
      AddCircleOutlined as AddCircleOutlinedIcon,
      IndeterminateCheckBox as IndeterminateCheckBoxIcon
    } from "@material-ui/icons";
    
    //FORMIK
    import { Formik, FieldArray, getIn, ErrorMessage } from "formik";
    import * as Yup from "yup";
    
    export default function UnitsDrawer(props) {
      const callAPI = e => {
        console.log(e.name);
        console.log(e.email);
      };
    
      const testSchema = Yup.object().shape({
        name: Yup.string().required("Customer name is required"),
        email: Yup.array().of(
          Yup.string()
            .email("Enter a valid email")
            .required("Email is required")
        )
      });
    
      const initialValues = {
        name: "",
        email: [""]
      };
    
      const formRef = React.useRef();
    
      return (
        <div>
          <Formik
            innerRef={formRef}
            validationSchema={testSchema}
            initialValues={initialValues}
            onSubmit={(values, actions) => {
              actions.setSubmitting(false);
              callAPI(values);
            }}
          >
            {({
              handleChange,
              handleBlur,
              values,
              errors,
              touched,
              handleSubmit,
              isSubmitting,
            }) => {
              return (
                <>
                  <div>
                    <TextField
                      label="Customer Name"
                      name="name"
                      margin="normal"
                      variant="outlined"
                      error={errors.name && touched.name}
                      onChange={handleChange}
                      onBlur={handleBlur}
                      value={values.name}
                      fullWidth
                    />
                    <ErrorMessage name="name" component="div" />
                  </div>
                  <FieldArray name="email">
                    {({ push, remove }) =>
                      values.email.map((field, index) => (
                        <div key={`${index}`} className="dynamic-fields">
                          <div>
                            <TextField
                              label="Email"
                              variant="outlined"
                              className="input-item"
                              error={
                                getIn(touched, `email.${index}`) &&
                                getIn(errors, `email.${index}`)
                              }
                              name={`email.${index}`}
                              value={values.email[index]}
                              onChange={handleChange}
                              onBlur={handleBlur}
                              fullWidth
                            />
                            <ErrorMessage name={`email.${index}`} component="div" />
                          </div>
                          <IconButton
                            aria-label="filter list"
                            className="add-icon"
                            onClick={() => {
                              push("");
                            }}
                          >
                            <AddCircleOutlinedIcon color="primary" />
                          </IconButton>
                          {values.email.length > 1 && (
                            <IconButton
                              aria-label="filter list"
                              className="add-icon"
                              onClick={() => {
                                remove(index);
                              }}
                            >
                              <IndeterminateCheckBoxIcon />
                            </IconButton>
                          )}
                        </div>
                      ))
                    }
                  </FieldArray>
                </>
              );
            }}
          </Formik>
        </div>
      );
    }