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

canSubmit is always true on first render of form #723

Closed
engelkes-finstreet opened this issue May 29, 2024 · 7 comments
Closed

canSubmit is always true on first render of form #723

engelkes-finstreet opened this issue May 29, 2024 · 7 comments

Comments

@engelkes-finstreet
Copy link

Describe the bug

I am using a form with the zodValidator and have several field-level validations. On the first render the canSubmit property is true even though several fields are invalid. After changing the first input the validations run and canSubmit is invalid.

Your minimal, reproducible example

https://tanstack.com/form/latest/docs/framework/react/examples/zod

Steps to reproduce

  1. Go to the official zod example
  2. The Submit button is enabled even though the firstName has a minLength validation of 3
  3. Type something in any of the fields
  4. The Submit button is disabled until all the validations are correct

Expected behavior

I would expect that the canSubmit property considers all validations on the first render and shows a disabled Submit button from the start instead of changing from enabled to disabled after typing one letter and going back to enabled after all validations are correct.

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

OS: MacOS
Browser: Arc

TanStack Form adapter

react-form

TanStack Form version

0.20.2

TypeScript version

No response

Additional context

No response

@Balastrong
Copy link
Member

Balastrong commented May 29, 2024

Hey! The current behavior in the example seems correct to me, the validator is triggered onChange hence I'd expect no validation to be run as long as the value doesn't change.

What you're trying to achieve might be validating onMount.


With that said, if we agree that onMount is the answer, I noticed that there might be a bug there preventing canSubmit to go false but it's another topic we might want to tackle (and I already have an idea why we have this bug).

An alternative option could be to manually run form.validateAllFields("change") when your form mounts.

@afriddev
Copy link

** I figured out the solution for canSubmit return true when your component renders,you need to validate all your form.Filed when it mounts **

function runOnMount({ value }: any) { form.validateAllFields("change"); return value; }

validators={{ onChange: (value) => { return handleMobileNumberChange(value?.value); }, onMount: runOnMount, }}

<form.Subscribe selector={(state) => [state.canSubmit, state.isSubmitting]} > {([canSubmit, isSubmitting]) => { return ( <Button disabled={!canSubmit} type="submit"> {!isSubmitting ? (!edit ? SUBMIT : MODIFY) : "Submiting ..."} </Button> ); }} </form.Subscribe>

@Glazy
Copy link

Glazy commented Jul 6, 2024

Not sure what the answer is here but I also encountered this same behaviour and find it confusing.

I validate my fields onChange (and sometimes onBlur) but I don't count the form as valid until every field has successfully ran it's validation(s).

I would've expected that disabling the "Submit" button based on isValid or canSubmit would be sufficient for this usage.

@OPerel
Copy link

OPerel commented Sep 22, 2024

I'm having the same issue. I'm trying to run validations at onChange (tried both at the form level and at the field level). The form is initially valid, both canSubmit and isValid are true, even though all fields are empty and thus invalid. Only when changing one of the values the form state is changed to invalid and canSubmit is false.
Using onMount for the initial validation doesn't help as this only runs when the form is mounted, causing the form to stay invalid even when fields are populated and the state changes.
All I want is to have the submit button disabled until the form is actually valid, which is a very simple and very common use case. I'm very surprised that it's so hard to achieve.

Hoping for a fix soon... 🙏
Thx

@brandonryan
Copy link

brandonryan commented Oct 29, 2024

Opinion:
Its the fields responsibility to choose whether or not to display the error, and the field indeed should be in error state on mount. The field can choose to only display its error when the component has been touched.

<Field
    name="resource"
    validators={{
        onMount: myValidator,
        onChange: myValidator,
    }}
    children={field => (
        <MyCustomInputComponent
            //...other field props
            errors={field.state.meta.isTouched ? field.state.meta.errors : []}
        />
    )}
/>

Then just use the same validator for onChange and onMount.

The fact that you have to specify the same function for onChange and onMount is a bit annoying for sure.
I have a helper function I use to simplify this for me. Just takes a function and returns the object form that tanstack is expecting.

@brandonryan
Copy link

brandonryan commented Oct 29, 2024

Correction: The above strategy wont work because onMount validator seems to mark the field as touched. I definitely think this is a bug. @Balastrong opinion here?

@Balastrong
Copy link
Member

Hey everyone, an update on this issue!

As mentioned at the beginning, it's correct that if there's only the onChange validator the form has canSubmit: true. Before any interaction the validator has never ran, thus the form is valid.

The recommended way is to run validation onMount too, but was broken until today as validation errors didn't go away. Now that #726 has been merged (update to version 0.34.4) you can safely use it, errors will go away as soon as a field is touched 🙌


One tiny detail remains, to have a first validation when the component mounts and keep validating when values are changed, you have to set the same validator to both onMount and onChange. Not a big deal but do you think we need a simple API to declare that and let form handle that internally? Let's discuss in #1005

Also, sorry if this took so long and thanks for all the feedback and examples!

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

6 participants