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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

isValid is sometimes false despite no errors #2755

Closed
krnlde opened this issue Aug 28, 2020 · 61 comments · Fixed by #2789
Closed

isValid is sometimes false despite no errors #2755

krnlde opened this issue Aug 28, 2020 · 61 comments · Fixed by #2789

Comments

@krnlde
Copy link
Contributor

krnlde commented Aug 28, 2020

Describe the bug
I sometimes get isValid = false despite having no visible errors and errors object being an empty object ({})

To Reproduce
I do not yet have a reproducable sandbox, but I already found a code piece that might be the problem. Please see additional context

Expected behavior
isValid should always reflect what the errors object contains (e.g. isValid = Object.keys(errors).length === 0)

Desktop (please complete the following information):

  • OS: Windows 10 202001
  • Browser: Microsoft Edge
  • Version: 86.0.621.0 (Official build) canary (64-bit)

Additional context
The problematic line could be this code piece here:

shouldRenderBaseOnError(name, error, skipReRender);
which triggers revalidation but without providing an isValid parameter as the 4th parameter. Hence this line
...(resolverRef.current ? { isValid: !!isValid } : {}),
will resolve in !!isValid which essentially means !!undefined === false.

AFAIK the problem only appears on async filled inputs (inputs that get their value via an api request).

@bluebill1049
Copy link
Member

Thanks @krnlde which version are you running?

@bluebill1049 bluebill1049 added the status: need more detail Please follow our issue template. label Aug 28, 2020
@krnlde
Copy link
Contributor Author

krnlde commented Aug 28, 2020

Latest, 6.6.0. But it is the same with the previous (6.5.3)

I can go back to the point where it worked and give the version.

@bluebill1049 bluebill1049 added bug Something isn't working and removed status: need more detail Please follow our issue template. labels Aug 28, 2020
@bluebill1049
Copy link
Member

would be great if you can have a codesandbox for me, no stress if you are busy. i will take a look at it over the weekend.

@krnlde
Copy link
Contributor Author

krnlde commented Aug 28, 2020

Alright! I'll try to do a reproducable codesandbox :)

@krnlde
Copy link
Contributor Author

krnlde commented Aug 28, 2020

Here's a simple variant with only 2 radios where isValid is initially false, but when changed it gets true: https://codesandbox.io/s/rhf-isvalid-initially-false-bug-3ucht

And here's a variant where the fields are required. The isValid field will always be false: https://codesandbox.io/s/rhf-isvalid-always-false-bug-forked-uvb30

It seems the problem is with radios. I also use radios in my usecase where it breaks.

@bluebill1049
Copy link
Member

bluebill1049 commented Aug 29, 2020

looks like this is not a new issue, i switched to 6.4.1. it has the same behavior.

@bluebill1049
Copy link
Member

bluebill1049 commented Aug 29, 2020

also, both example doesn't have mode: 'onChange', perhaps you are after one of them is checked with validate? I think required: true with each radio/checkbox is perhaps the issue here. https://codesandbox.io/s/checkbox-min-checked-5vocd

@bluebill1049 bluebill1049 added status: under investigation aware of this issue and pending for investigation and removed bug Something isn't working labels Aug 29, 2020
@krnlde
Copy link
Contributor Author

krnlde commented Aug 29, 2020

Hey! I have that for a while now, but it got worse somewhere between 6.4 and 6.6.

I'm not sure right now which mode I'm using, but I assume onChange.

@bluebill1049
Copy link
Member

bluebill1049 commented Aug 29, 2020

oh, I see, so 6.4 prior is working? I will check it out. I could be related to bug fix.

@krnlde
Copy link
Contributor Author

krnlde commented Aug 29, 2020

Unfortunately I didn't recognize exactly when it started to happen... When I'm back on my PC I can "time travel" and investigate

@bluebill1049
Copy link
Member

Don't worry about it @krnlde leave it to you, i will let you know.

@bluebill1049
Copy link
Member

https://codesandbox.io/s/rhf-isvalid-initially-false-bug-forked-sjifc?file=/src/App.tsx
https://codesandbox.io/s/rhf-isvalid-always-false-bug-forked-fsk5l?file=/src/App.tsx

switched on mode: 'onChange' isValid is working for me. I think those two CSB is not reproducing the issue.

@bluebill1049
Copy link
Member

I may need your help with a reproducible CSB. 🙏

@krnlde
Copy link
Contributor Author

krnlde commented Aug 29, 2020

Will do! Thank you :)

Regarding not having the mode set to onChange: Shouldn't it be true initially anyways though?

@bluebill1049
Copy link
Member

Will do! Thank you :)

Regarding not having the mode set to onChange: Shouldn't it be true initially anyways though?

both codesandboxs are returning true after i attached mode: 'onChange' :)

@krnlde
Copy link
Contributor Author

krnlde commented Aug 31, 2020

Here I got the exact problem I have. It's a bit tricky to force the isValid: false state, but when you just switch between the first and the second radio, it should appear after 1 second (the current timeout delay, to emulate network request). https://codesandbox.io/s/rhf-isvalid-always-false-bug-forked-xgt5u?file=/src/App.tsx

@bluebill1049
Copy link
Member

Thanks very much, @krnlde 👍 I will take a look at it tmr night.

@bluebill1049
Copy link
Member

This issue is related to disabled, I think it makes sense to read the value during validation but not at submit.

@bluebill1049 bluebill1049 added bug Something isn't working and removed status: under investigation aware of this issue and pending for investigation labels Sep 1, 2020
@bluebill1049
Copy link
Member

by the way, please read this: https://twitter.com/bluebill1049/status/1300231640392716288 just be careful with disabled input for submission. we are following the HTML standard in this lib.

bluebill1049 added a commit that referenced this issue Sep 1, 2020
bluebill1049 added a commit that referenced this issue Sep 1, 2020
* fix #2785 issue with async nested field array reset

* fix #2755 with disabled input

* improve getFieldValue API

* fix fieldArray dep and fix variable name for ref

* remove unneeded dep

* upgrade packages

* include unit test
@noonii
Copy link

noonii commented Nov 5, 2021

I've faced the same issue regarding react-hook-form built with material-ui (legacy) or @mui (latest) , here's the CSB

https://codesandbox.io/s/react-hook-form-issue-2755-r5j7k

Reproducible in following versions of react-hook-form : 7.17.5, 7.19.0

@bluebill1049

EDIT:
Please re-open the issue or I can create a new ticket if preferred.
Discovered issue started at >= v7.14.0 and was working as expected in v7.13.0

@bluebill1049
Copy link
Member

formState is wrapped with a Proxy to improve render performance and skip extra logic if the specific state is not subscribed to. Therefore make sure you invoke or read it before a render in order to enable the state update.

https://react-hook-form.com/api/useform/formstate

const { isValid } = methods.formState;

console.log(isValid);

@mcmxcdev
Copy link

I am running into this issue too. It works with mode: 'all', reValidateMode: 'onChange', but we don't want to trigger validations before submitting the form first.

Destructuring formState as described above also doesn't seem to work.

@kelleyperry
Copy link

for me it is not that the functionality does not work, it is the tests that are failing when using "isValid" in the check.

@Nziranziza
Copy link

Having similar issue now was there any solution

@nir099
Copy link

nir099 commented Jun 29, 2022

hack until this is fixed.

we can assert form validity by not having errors.

isEmpty(methods.formState.errors)

for initial validation failure
we can trigger validations by

 useEffect(() => {
    methods.trigger();
  }, []);

@GProst
Copy link
Contributor

GProst commented Aug 11, 2022

Having this issue on v7.34.0, destructuring and onChange mode deosn't help

@bluebill1049
Copy link
Member

Having this issue on v7.34.0, destructuring and onChange mode deosn't help

could you share a simple codesandox?

@GProst
Copy link
Contributor

GProst commented Aug 12, 2022

@bluebill1049 as you can see initially isValid is false but errors object is empty: https://codesandbox.io/s/recursing-mopsa-qvyt6g?file=/src/App.js

@bluebill1049
Copy link
Member

That's expected @GProst errors state only start to update once user start to interact with the input/form, while isValid report the entire form valid state.

@GProst
Copy link
Contributor

GProst commented Aug 12, 2022

@bluebill1049 ahh, sorry for false alarm then

@bluebill1049
Copy link
Member

bluebill1049 commented Aug 12, 2022

No worriees, @GProst if you want to force the errors state to be updated/rendered, you can try to call trigger() (at useEffect), that would validate the whole form and flush down those errors state

@ryu-liam
Copy link

Putting trigger() inside of useEffect works for me!

@Moshyfawn
Copy link
Member

Yes, this is the recommended way to validate defaultValues

@franksimon90
Copy link

@bluebill1049 Sorry to spam a closed ticket, but since it has some activity. RFC: How's the new useFieldArray rules going to play with this errors behavior and refs, currently using formState errors with { mode: 'onChange'} is far from usable, isValid is going to be true even with errors being shown on the UI, for more than a few use cases, using a trigger isn't logical, basically means to re-execute the rules (that in my case are async ones).

@soulbeing
Copy link

call trigger() sort of works, but at the same time it checks all the other fields and resulted in error messages for all the other fields that user hasn't enter anything yet. What to do? Any expectation when this issue can be fixed?

@Moshyfawn
Copy link
Member

call trigger() sort of works, but at the same time it checks all the other fields and resulted in error messages for all the other fields that user hasn't enter anything yet. What to do? Any expectation when this issue can be fixed?

You can pass the field name / list of names to the trigger method instead. See the trigger RHF docs section to learn more

@dimitriBouteille
Copy link

dimitriBouteille commented Oct 6, 2022

Hi @soulbeing If you want re-validate or validate form without display errors, you can do this :

const {trigger,clearErrors,} = useForm<FormData>({
  resolver: yupResolver(...),
});

...

trigger(...).then(() => clearErrors());

@briangwaltney
Copy link

Just another data point for analysis, I have this error when using autocomplete from my browser in an input field. When I type, no issues. When I just select the suggested option, no errors and !isValid.

@wansiedler
Copy link

Just another data point for analysis, I have this error when using autocomplete from my browser in an input field. When I type, no issues. When I just select the suggested option, no errors and !isValid.

How did you fix it?
Got the same problem.

@briangwaltney
Copy link

I ended up refactoring my form so I didn't need to use the isValid api. Now it goes through the normal check procedure on submit. I know that's not very helpful, but it's what worked for me.

@wansiedler
Copy link

I ended up refactoring my form so I didn't need to use the isValid api. Now it goes through the normal check procedure on submit. I know that's not very helpful, but it's what worked for me.

I now realised that I have a big Yup validation scheme and in my stepper From only few are being analysed. This is why the Form is !isValid.

@bymoe
Copy link

bymoe commented Dec 8, 2022

Why is this closed when the issue still exists?

@rHilkner
Copy link

rHilkner commented Dec 30, 2022

Using workaround of lodash isEmpty function: isEmpty(form.formState.errors)

@stefanpl
Copy link
Contributor

stefanpl commented Jan 2, 2023

@bluebill1049 being confused about this as well, I just watched your video on formState.

I've noticed that here in the video, an older version of the docs is showing, which is giving a crucial hint about isValid:

This state is only applicable with onChange and onBlur mode.

Why has this been removed from the docs? My impression is, this is still the case, and documenting it might be super helpful, as shown by the activity in this issue.

If you agree, I'll add it back in.

@bluebill1049
Copy link
Member

@stefanpl This has been improved: https://github.com/react-hook-form/react-hook-form/releases/tag/v7.39.0

@bluebill1049
Copy link
Member

Actually in my case, what solved is to check form.control._formState.errors instead of form.formState.errors. Looks like form.formState.errors is taking 1 more render to appear for me and the form.control._formState.errors had the errors populated in the same render. The thing is that my formState was really invalid, but the errors just were not showing up.

I think you should wait for the next render to flush out the updated formState and reflect on the UI.

@rHilkner
Copy link

rHilkner commented Jan 5, 2023

In the end using form.control._formState.errors didnt solve all my issues also...

Here's my issue:
I'm not sure how do I wait for the next render since I need to perform the validation programatically when a button is clicked. I have this function below, that should return if my formulary is currently valid or not. The current behaviour is that it needs to be clicked twice to work.

instantiation of the form

  const form = useForm({
    mode: "onBlur",
    resolver: yupResolver(formData.schema),
    defaultValues: formData.defaultValues(getLocalStorageItem("reservation")),
  })

Validation function

  const validateForm = async (): Promise<boolean> => {
    console.log(form.getValues())
    await form.trigger()
    if (form.formState.isValid) {
      return true
    }
    if (isEmpty(form.formState.errors)) {
      console.error("Error in the form")
    } else {
      console.error(form.formState.errors)
    }
    return false
  }

And is called by

  const onButtonClick = async () => {
    if (!await validateForm()) return
    setLoading(true)
    makeRequest(...).then(...).finally(setLoading(false))
  }

In the first click in the button it prints "Error in the form" and in the second time it performs the request successfully. Both times the object printed by form.getValues() is the same. Any ideas on the fix? I thought calling .trigger() would solve my problems, but now I've ran out of ideas.

@rHilkner
Copy link

rHilkner commented Jan 5, 2023

So the fix for me was reading form.formState.errors before performing the trigger(), even though the errors are always empty. Reading the errors made the trigger() execute correctly and updated the form.formValues properties.

  const validateForm = async (): Promise<boolean> => {
    const _ = form.formState.errors // also works if you read form.formState.isValid
    await form.trigger()
    if (form.formState.isValid) {
      return true
    }
    if (isEmpty(form.formState.errors)) {
      console.error("Error in the form")
    } else {
      console.error(form.formState.errors)
    }
    return false
  }

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.