40

I'm making a form with React, Formik, react-bootstrap, and yup for validation. I am trying to display validation errors, but the touched property is not being populated with the fields.

const schema = yup.object({
  name: yup.string().required(),
  email: yup
    .string()
    .email()
    .required(),
});

const ChildForm = props => {
  const { child: { name = '', email = '' } = {} } = props;
  const submitHandler = ({name, email}) => console.log(name, email);

  return (
    <Formik
      validationSchema={schema}
      onSubmit={submitHandler}
      initialValues={{ name, email }}
      render={({ handleSubmit, handleChange, values, touched, errors }) => 
      {
        console.log('touched: ', touched);
        return (
          <Form noValidate className="mt-4" onSubmit={handleSubmit}>
            <Form.Row>
              <Form.Group as={Col} controlId="name">
                <Form.Label>Full Name</Form.Label>
                <Form.Control
                  name="name"
                  required
                  value={values.name}
                  onChange={handleChange}
                  isValid={touched.name && !errors.name}
                  isInvalid={touched.name && errors.name}
                  type="text"
                  placeholder="Your child's name"
                />
                <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
                <Form.Control.Feedback type="invalid">
                  {errors.name || 'Please enter your child\'s name'}
                </Form.Control.Feedback>
              </Form.Group>
            </Form.Row>
            <Form.Row>
              <Form.Group as={Col} controlId="email">
                <Form.Label>Email Address</Form.Label>
                <Form.Control
                  name="email"
                  required
                  value={values.email}
                  onChange={handleChange}
                  isValid={touched.email && !errors.email}
                  isInvalid={touched.email && errors.email}
                  type="text"
                  placeholder="Your child's email address"
                />
                <Form.Control.Feedback>
                  No spam, we promise!
                </Form.Control.Feedback>
                <Form.Control.Feedback type="invalid">
                  {errors.email || 'Please enter a valid email address'}
                </Form.Control.Feedback>
              </Form.Group>
            </Form.Row>
            <Form.Row className="float-right">
              <Button variant="success" onClick={handleSubmit}>
                <Icon icon={faSave} />
                &nbsp; Submit
              </Button>
            </Form.Row>
          </Form>
        );
      }}
    />
  );
}

What am I doing wrong here? The console.log(touched) always shows an empty object.

djheru
  • 3,525
  • 2
  • 20
  • 20

2 Answers2

48

@djheru Your solution is correct because Formik sets touched flags on blur event instead of on change. Here is Formik author comment about this: You have to call Formiks handleBlur to notify Formik that blur event has been triggered - so yes, these handlers are needed.

Bartek Fryzowicz
  • 6,464
  • 18
  • 27
37

I got it working by accessing the handleBlur function that's passed in the render function argument, and adding that as an onBlur handler for each of the form elements. Not sure if that's needed because I'm using react-bootstrap form components, but the react-bootstrap docs have a Formik example, but the touched object was not getting updated.

(
  <Formik
    validationSchema={schema}
    onSubmit={submitForm}
    initialValues={{ name, email }}
    render={({
      handleSubmit,
      handleChange,
      handleBlur, // handler for onBlur event of form elements
      values,
      touched,
      errors,
    }) => {
      return (
        <Form noValidate className="mt-4" onSubmit={handleSubmit}>
          <Form.Row>
            <Form.Group as={Col} controlId="nameControl">
              <Form.Label>Full Name</Form.Label>
              <Form.Control
                name="name"
                required
                value={values.name}
                onChange={handleChange}
                onBlur={handleBlur} // This apparently updates `touched`?
                isValid={touched.name && !errors.name}
                isInvalid={touched.name && errors.name}
                type="text"
                placeholder="Your child's name"
              />
              <Form.Control.Feedback>Looks good!</Form.Control.Feedback>
              <Form.Control.Feedback type="invalid">
                {errors.name || 'Please enter your child\'s name'}
              </Form.Control.Feedback>
            </Form.Group>
          </Form.Row>
Alexander Nied
  • 12,804
  • 4
  • 25
  • 45
djheru
  • 3,525
  • 2
  • 20
  • 20
  • 3
    You saved me a lot of trouble! Can you believe it, the official Bootstrap-React tutorial that shows how to validate forms with Formik is incorrect, and doesn't have this detail! https://react-bootstrap.github.io/components/forms/#forms-validation-libraries – gene b. Mar 18 '21 at 00:56