Formik: [v2] Validation runs on old values after setFieldTouched

Created on 2 Dec 2019  ·  30Comments  ·  Source: formium/formik

🐛 Bug report

Current Behavior

Calling setFieldTouched runs validation on old values.

Expected behavior

Validation should be called on new values.

Reproducible example

https://codesandbox.io/s/formik-codesandbox-template-yqrgc

I can get around this issue with replacing

props.setFieldValue("name", "John");
props.setFieldTouched("name", true);

with

props.setFieldValue("name", "John");
// Set `shouldValidate` to `false` to prevent validation
props.setFieldTouched("name", true, false); 
// Call validation with the new values
props.validateField(name);

Additional context

Related problems I could find: #1977, #2025

Your environment

| Software | Version(s) |
| ---------------- | ---------- |
| Formik | 2.0.6 |
| React | 16.12.0 |
| Browser | Google Chrome 78 |
| npm/Yarn | Yarn 1.19.0 |
| Operating System | Fedora 31 |

High Bug

Most helpful comment

you can use the following, it worked for me

setTimeout(() => setFieldTouched(value, true))

Wrap setFieldTouched in a setTimeout() function

All 30 comments

Not sure if the #2116 should have closed this issue or not, but i believe this is still a problem in 2.1.1

Reproducible example
https://codesandbox.io/s/formik-codesandbox-template-yqrgc

Upgrading formik to 2.1.1 in the reproducible example in the OP still shows the same behaviour

Both setXXX will are called synchronously in your example. setFieldTouched thus doesn’t wait for setFieldValue. Because hooks are annoying af, there is no way for us to provide a promise or callback after the commit AFAIK. Thus my suggestion is then to call setFieldValue first and let it run validation as side effect and then call setFieldTouched but abort validation.

My point above is that this _isn’t_ a bug, it’s how React works.

Both setXXX will are called synchronously in your example. setFieldTouched thus doesn’t wait for setFieldValue. Because hooks are annoying af, there is no way for us to provide a promise or callback after the commit AFAIK. Thus my suggestion is then to call setFieldValue first and let it run validation as side effect and then call setFieldTouched but abort validation.

Thank you for the explanation!
I'm not sure it falls under the responsibilities of Formik since the behavior is the result of how React works, but maybe it would help people if it's documented in Formik so it's less confusing?

My point above is that this _isn’t_ a bug, it’s how React works.

I understand that, but isn't there way, how to avoid this behavior if setFieldValue internally sets field is touched (maybe optionally)?

Or what is, in this case, the best way how to create custom Formik-hooked components that need to set the value (of course) as well as set they're touched - e.g. I don't want to validateOnChange, so validation runs when touched is set and therefore validation is run on old value.

Thanks for any insights

I'm running into something similar, but it's with validation? Can someone help since this was working fine in Formik 1.x, but is now validation in Formik 2.x never seems to work correctly when using setFieldValue.

I tried the recommendation of using setFieldValue, then setFieldTouched and then validateField, but the field I am using is still not valid for some reason.

My code looks like so (a custom Field.)


Form.validationSchema = Yup.object().shape({
  size: Yup.string()
    .required('Size is required.'),
});

<Select
      options={options}
      name={field.name}
      value={foundOption}
      onChange={(option) => {        
        form.setFieldValue(field.name, option.value);
        form.setFieldTouched(field.name, true, false);
        form.validateField(field.name);        
        onChange(option.value);
       }}
      onBlur={field.onBlur}

In this case, 'Size is required' is showing up, even though I am setting a new value. The only time it starts to validate correctly is when I click away from the