Tuesday, July 20, 2021

formik/yup problem: validating wrong value

 Odd geeky problem from work, we have components that support Formik - specifically if they find themselves in a FormikContext they take their values from there.

So one thing that's easy to miss if you're not paying attention is Formik components probably shouldn't display their error message unless they have been "touched" (submitting the form counts as a "touch" for each element. And of course, if you are making your own components, they should set "touched" once they are interacted with - the easiest way is to use the FormikContext.handleBlur(event), but some of our made up components - specifically some radio buttons - didn't have the "event" needed (a non-input DOM element was handling the click event), so I had to call FormikContext.setFieldTouched() directly. 

But we were getting weird results... we had Yup have the field as required, and even though a radio value was selected the error message for Required was still showing up.

We'd already built a "Formik Peek" component:

const FormikPeek = () => {
  const formikContext = useContext(FormikContext);
  const valuesString = formikContext && JSON.stringify(formikContext.values);
  const errorsString = formikContext && JSON.stringify(formikContext.errors);
  const touchedString = formikContext && JSON.stringify(formikContext.touched);
  return (
    <table>
      <tbody>
        <tr>
          <th>values</th>
          <td>{valuesString}</td>
        </tr>
        <tr>
          <th>errors</th>
          <td>{errorsString}</td>
        </tr>
        <tr>
          <th>touched</th>
          <td>{touchedString}</td>
        </tr>
      </tbody>
    </table>
  );
};

And that was showing that the value was being updated ok, but Yup was being run.

So this is what we had as our onChange equivalent:

  const localOnChange: React.ChangeEventHandler<HTMLInputElement> = (event) => {
    onChange && onChange(event);
    formikContext && formikContext.setFieldValue(componentName, value);
    formikContext && formikContext.setFieldTouched(componentName, true, true);
  };

So, the trouble was the second "true" to setFieldTouch, which says "validate this now". That was somehow being read as "validate with the old value"- maybe because the setFieldValue and setFieldTouched was in the same thread? I dunno. But even with validation set to false, it still seemed to validate at about the right time, so I'm not sure when "true" is useful. 

No comments:

Post a Comment