Question

Why isn't the Formik `touched` property being populated?

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.

 46  63185  46
1 Jan 1970

Solution

 58

@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.

2019-08-13

Solution

 43

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>
2019-08-07