Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling setFieldValue in response to a field change is out of sync with validations #2059

Closed
agmcleod opened this issue Nov 22, 2019 · 36 comments

Comments

@agmcleod
Copy link

agmcleod commented Nov 22, 2019

馃悰 Bug report

Current Behavior

The example im working with is let's say i have two drop down menus 1 & 2.

When menu1 changes, i want to reset the value of menu2. When I do this, and both fields are required, both menu1 & menu2 are invalid, even though menu1 has a value.

Expected behavior

I am updating the value of menu2 in componentDidUpdate when formik.values.menu1 changes. I feel like calling setFieldValue('menu2,' '') should be safe.

Reproducible example

https://gist.github.com/agmcleod/921c5798c4ba7285bc12bff8ebd6d82b

I tried to use the code sandbox templates, but yup validations werent working there for some reason.

Suggested solution(s)

Your environment

Software Version(s)
Formik 2.0.6
React 16.12.0
Browser Firefox
npm/Yarn yarn 1.13.0
Operating System MacOS Mojave
@GarrisonD
Copy link

Looks like I experienced the same issue today after migrating from formik 1.5 to 2.0

@njetsy
Copy link

njetsy commented Nov 26, 2019

The gist is not loading for me on my network but I was having a similar issue and figured I'd mention it in case it helps someone else while upgrading....

I had multiple selects using setFieldValue but the validation always seemed to be one step behind. In my change handler I was calling setFieldValue and then setFieldTouched to touch and set the value of the field. Both of these functions trigger the validate function and it looks like the setFieldTouched function was returning last and running using the value of the field before it was updated. This showed the field as invalid even though it had a value.

In short I just updated the setFieldTouched function to not validate the form.

// Set field to touched but do not trigger validation
props.form.setFieldTouched(field.name, true, false);

@agmcleod
Copy link
Author

@njetsy i did consider that, but i also wanted the form validating still, showing the user they need to fill out the second drop down if they already submitted.

In my case i wrapped my setFieldValue() call in a requestAnimationFrame callback, which is very much a hack.

@njetsy
Copy link

njetsy commented Nov 26, 2019

I just found another form that's probably closer to yours where I have to clear menu 2 after selecting a new menu 1 option. When I call setFieldValue to assign the newly selected value to menu 1, it runs the yup validation successfully with the correct values. When I then call the second setFieldValue to clear menu 2, a console log within the yup validation shows the menu 1 value as still being null so it shows the field as invalid.

@arianitu
Copy link

I'm running into a similar issue. I have a select dropdown (only 1.) and when I call form.setFieldValue(), the validation runs, but it fails for some reason until I click somewhere on the screen.

I'm also running into this after upgrading to Formik 2.x

@arianitu
Copy link

In my case, I enabled validateOnMount and I had an onChange handler that caused a re-render on which then called validate on the initialValues instead of the actual values.

@stale stale bot added the stale label Mar 17, 2020
@sapkra
Copy link

sapkra commented Apr 13, 2020

I had a similar issue and solved it by calling setFieldTouched in a timeout.

const handleChange = (value: string) => {
  formik.setFieldValue(name, value);
  setTimeout(() => formik.setFieldTouched(name, true));
};

@stale stale bot removed the stale label Apr 13, 2020
@vicasas
Copy link

vicasas commented Jun 6, 2020

I had a similar issue and solved it by calling setFieldTouched in a timeout.

const handleChange = (value: string) => {
  formik.setFieldValue(name, value);
  setTimeout(() => formik.setFieldTouched(name, true));
};

worked me

Is there any other solution because it is ultimately a problem or not?

@silverrajat88
Copy link

I am also facing the same issue with "formik": "^2.1.4".
any solution for this apart from using a timeout, which seems really hacky.

@tnielsenac
Copy link

tnielsenac commented Jul 9, 2020

I've also run into this problem, also with formik ^2.1.4. The workaround below is working for me as well.

I had a similar issue and solved it by calling setFieldTouched in a timeout.

const handleChange = (value: string) => {
  formik.setFieldValue(name, value);
  setTimeout(() => formik.setFieldTouched(name, true));
};

@bswank
Copy link

bswank commented Aug 26, 2020

I find that I have to call validateField and setFieldTouched in my onBlur within a timeout in order to get it to work. This is so I can validate individual fields on blur. Any news on if the settimeout hack will no longer be needed at some point?

@SaharHaltzi
Copy link

Pay attention that function setFieldValue in formik is an async function(it returns a Promise), therefore, when you want to change x field value as a result of y field , you have to put the calling for setFIeldValue on y field in a callback to the first setFieldValue call: for example:

onChange= (_, value) => {
setFIeldValue(x, value).than( (res) =>{
setFieldValue(y, /* your desired value */)
}
}

(or using async await syntax sugaring)

@jjarcik
Copy link

jjarcik commented Sep 29, 2020

I had a similar issue and solved it by calling setFieldTouched in a timeout.

const handleChange = (value: string) => {
  formik.setFieldValue(name, value);
  setTimeout(() => formik.setFieldTouched(name, true));
};

worked me

Is there any other solution because it is ultimately a problem or not?

Solution without setTimeout is here:

const handleChange = async (value: string) => {
  await formik.setFieldValue(name, value);
  formik.setFieldTouched(name, true)
};

@sjungwirth
Copy link

^ it seems the typescript definition for setFieldValue is wrong; it shows return value as void but this fix worked great for me when nothing else did.

@mmeinzer
Copy link

mmeinzer commented Oct 6, 2020

@sjungwirth Is there a simple workaround to give typescript the correct definition? I'm running into the same issue with this.

@sapkra
Copy link

sapkra commented Oct 7, 2020

There are PRs which are changing this type + docs but they are still not merged:
#2648
#2384

@drudv
Copy link

drudv commented Oct 28, 2020

For some reason the solution without setTimeout doesn't work for me. But it works fine with delayed setFieldTouched

@mattrabe
Copy link

mattrabe commented Nov 3, 2020

@sapkra @jjarcik @bswank @drudv and others using either the setTimeout method or async awaiting setFieldValue: Have you tried using formik.handleChange(key)(value) (in onChangeText) or formik.handleBlur(key)(event) (in onBlur) instead of calling setFieldTouched and setFieldValue separately? This will require having validateOnChange/validateOnBlur set to true

@ivos
Copy link

ivos commented Dec 19, 2020

I was just bitten by this issue and await on setValue helped me. I was actually considering it before, but bc the docs says it's return value is "void" I didn't even bother trying it out.

I'm confused on what grounds has this issue been closed. There is no resolution or recommendation attached to closing it. I would expect to at least fix the bug in the docs or ideally provide a recommendation or even a link to the docs where this is explained.

(On a higher level, an API where one has to await for setValue before being able to call setTouched, and if not then there is no exception reported, but the setValue silently does not work, looks brittle at least, if not wrong.)

@nirvparekh
Copy link

this guy has awesome solution https://stackoverflow.com/a/57684307/4445575

@terrynguyen255
Copy link

I'm still getting this issue at version 2.2.9
Fixed by @sapkra 's approach
However, it makes code long and not descriptive. I hope there will be a patch for it soon :)

onChange={(newValue) => {
    setFieldValue('my-field-name', format(newValue), true); // 3rd param means should validate, but it's not working, I guess
    setTimeout(() => setFieldTouched('my-field-name', true)); // this fixes the issue
}

@sohammondal
Copy link

sohammondal commented Sep 15, 2021

I had a similar issue and solved it by calling setFieldTouched in a timeout.

const handleChange = (value: string) => {
  formik.setFieldValue(name, value);
  setTimeout(() => formik.setFieldTouched(name, true));
};

worked me
Is there any other solution because it is ultimately a problem or not?

Solution without setTimeout is here:

const handleChange = async (value: string) => {
  await formik.setFieldValue(name, value);
  formik.setFieldTouched(name, true)
};

setTimeout solved the issue. thanks!

@StasNemy
Copy link

If you need validate a few values after change one value, you can use Promise.all

const handleChange = (
  formik,
  value,
  [name0, name1, name2, name3, name4, name5] // will best use param and in promise use param.map
) => {
  const setAllFields = Promise.all([
    formik.setFieldValue(name0, value),
    formik.setFieldValue(name1, getName1(value)),
    formik.setFieldValue(name2, getName2(value)),
    formik.setFieldValue(name3, getName3(value)),
    formik.setFieldValue(name4, getName4(value)),
    formik.setFieldValue(name5, getName5(value)),
  ]);

  setAllFields.then((res) => {
    Object.keys(res[0]).forEach((key) => {
      formik.setFieldTouched(key, true);
    });
  });
};

@aminedammak
Copy link

Pay attention that function setFieldValue in formik is an async function(it returns a Promise), therefore, when you want to change x field value as a result of y field , you have to put the calling for setFIeldValue on y field in a callback to the first setFieldValue call: for example:

onChange= (_, value) => { setFIeldValue(x, value).than( (res) =>{ setFieldValue(y, /* your desired value */) } }

(or using async await syntax sugaring)

This solved the issue for me. Thank you!

@skusebauch
Copy link

I had the same "trouble" and saw this issue here at GitHub today, there is still no update in the docs and types.d.ts-file of the packages, at least what I can see?!

still:
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => void;
instead of:
setFieldValue: (field: string, value: any, shouldValidate?: boolean) => Promise<void>;

I had to overwrite the types in my project manually and then it works fine with async-await - but setFieldTouchedworks as expected as void without `Promise, the potential of causing some issues is high if you come back to your code later, so always need to provide comments with the link to this issue:

setFieldValue={
  setFieldValue as (
    field: string,
    value: unknown,
    shouldValidate?: boolean ,
  ) => Promise<void>
} // setFieldValue is a promise we need to use await - https://github.com/jaredpalmer/formik/issues/2059

Are there any plans to update this - or do I miss something? :)

@T04435
Copy link

T04435 commented Jul 7, 2022

I run into this.

My solution, call setFieldTouched(name) before setFieldValue(name, value, true).
Then the validation will work as normal :)

@kamiloox
Copy link

does Formik receive any updates on this topic and in general in a whole codebase?
I overcame this problem by doing my own setFieldValueAndValidate function. It would be really awesome if this will be marked as an open issue 鉁岋笍

const setFieldValueAndValidate = async (field: string, value: unknown) => {
  await setFieldValue(field, value);
  validateField(field);
};

@NathanC
Copy link

NathanC commented Aug 10, 2022

Just fyi, I'm still getting this on v2.2.9. I solved it using the setTimeout on every setFieldTouched call. I wasn't able to test using await, as I'm using typescript and the type signature shows it just returning void, not a Promise. :)

@waymaha
Copy link

waymaha commented Aug 30, 2022

setTimeout didn't work in my case, I used useEffect to call formik.validate() when formik.values changed. Yea i know it's a lot messy but it forces formik to revalidate and keep itself in sync.

@glanzz
Copy link

glanzz commented Oct 6, 2022

Shouldn't the setFieldTouched() be called inside the setFieldValue() as part of default implementation of setFieldValue ?

@brandonlenz
Copy link

brandonlenz commented Dec 6, 2022

2.2.9 has been released for a long time as the latest version, and this issue still exists.

My project was "working" accidentally because of a useEffect block that was effectively triggering on every re-render which called validate() similar to what @waymaha suggested.

"Fixing" that useEffect causes this issue to occur.

Should I be using a different version of Formik at this point? Is Formik still maintained? I see that the PR mentioned a couple of years ago that fixes the type definition is open: #2384

Using set timeout is hacky. A useEffect block is a little less hacky, but still not great since you have to remember to add this useEffect to get what should be default behavior.

@brandonlenz
Copy link

@jaredpalmer would you mind re-opening this issue please?

@brandonlenz
Copy link

brandonlenz commented Dec 6, 2022

Anyways, for others coming across this, I was able to get it to work with the following:

       const handleMyFieldChange: ChangeEventHandler<
          HTMLInputElement
        > = async (e) => {
          if (e.target.value === 'no') {
            await setFieldValue('my_other_field', DEFAULT_VALUE)
            setFieldTouched('my_other_field', DEFAULT_TOUCHED)
          }
        }

Where DEFAULT_VALUE is some default value I want my fields to clear to when hidden, and DEFAULT_TOUCHED is false.

The await is key here, which the type definition does not include (see PR linked in comment above)

@Mohammad-Ravand
Copy link

if(action.action =="select-option"){ form.values['parentCategory']=option.value }

Jibz-Mysten added a commit to MystenLabs/sui that referenced this issue Apr 19, 2023
## Description 

Describe the changes or additions included in this PR.

Fix the error we use `setFieldValue` in Formik leading to validation
error see [issue](jaredpalmer/formik#2059)

## Test Plan 

Before


https://user-images.githubusercontent.com/126525197/233100066-e41f0173-e544-4f66-b6eb-da156d84b2d2.mov

After


https://user-images.githubusercontent.com/126525197/233100092-1c0a772a-7442-48d6-b2d8-7db384a854a3.mov


How did you test the new or updated feature?

---
If your changes are not user-facing and not a breaking change, you can
skip the following section. Otherwise, please indicate what changed, and
then add to the Release Notes section as highlighted during the release
process.

### Type of Change (Check all that apply)

- [x] user-visible impact
- [ ] breaking change for a client SDKs
- [ ] breaking change for FNs (FN binary must upgrade)
- [ ] breaking change for validators or node operators (must upgrade
binaries)
- [ ] breaking change for on-chain data layout
- [ ] necessitate either a data wipe or data migration

### Release notes
@marano
Copy link

marano commented Nov 27, 2023

I am having this same issue on Formik latest version is 2.4.5 馃樋 This should definetely be reopened.

@brandonlenz 's workaround did work for me, but the impression I get is that touched doesn't really have anything to do with the problem, the fix works simply because it retriggers the validation which should be handled by setValue in the first place. It is the same workaround as calling setValue again on a setTimeout.

@vkoman
Copy link

vkoman commented Dec 19, 2023

I am having the same issue using "formik": "^2.4.5"
adding setFieldTouched does not work on Safari, it works only in Chrome

setFieldValue("password", '');
setTimeout(() => setFieldTouched("password", true));

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests