TanStack / form

🤖 Powerful and type-safe form state management for the web. TS/JS, React Form, Solid Form, Lit Form and Vue Form.
https://tanstack.com/form
MIT License
3.83k stars 348 forks source link

[BUG] When using zodValidator, even if I set a valid value with setFieldValue, canSubmit does not become true #999

Open koki-iwaizumi opened 1 month ago

koki-iwaizumi commented 1 month ago

Describe the bug

When using zodValidator, even if I set a valid value with setFieldValue, canSubmit does not become true

import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'

// ...

const formSchema = z.object({
  age:   z.string().optional().refine((data) => data != null && data !== '' && !isNaN(Number(data))),
  ageSub:   z.string().optional().refine((data) => data != null && data !== '' && !isNaN(Number(data))),
})

const form = useForm({
  validatorAdapter: zodValidator(),
  validators: {
    onChange: formSchema
  },
})

const onChangeAge = (handleChange) => (event: React.ChangeEvent<HTMLInputElement>) => {
  handleChange(event.target.value)
  form.setFieldValue('ageSub', event.target.value)  // The validation for ageSub is not being executed here.
}

return (
  <form>
      <form.Field name="age">
        {(field): React.ReactNode => {
          return (
            <Input
              type="number"
              onChange={(e): void => onChangeAge(field.handleChange)}
              value={field.state.value?.toString()}
            />
          )
        }}
    </form.Field>
    <form.Subscribe
      selector={(state): { isDisabled: boolean } => {
        return { isDisabled: state.isPristine || !state.canSubmit }
      }}
    >
      {({ isDisabled }): React.ReactNode => {
        return (
          <Button isDisabled={isDisabled} >
            submit
          </Button>
        )
      }}
    </form.Subscribe>
  </form>
)

In version 0.32.0, canSubmit became true with valid values (e.g., '5' or '6'), but in version 0.33.0, canSubmit remains false.

Expected behavior

I want validation to be executed when setFieldValue is called.

How often does this bug happen?

Every time

Platform

TanStack Form adapter

react-form

TanStack Form version

v0.33.0

TypeScript version

v5.6.3

Additional context

If I execute validateField after every setFieldValue call, the validation runs and behaves as expected. However, this was not necessary in version 0.32.0. I would like to know if this behavior is intentional or a bug.

like this.

import { zodValidator } from '@tanstack/zod-form-adapter'
import { z } from 'zod'

// ...

const formSchema = z.object({
  age:   z.string().optional().refine((data) => data != null && data !== '' && !isNaN(Number(data))),
  ageSub:   z.string().optional().refine((data) => data != null && data !== '' && !isNaN(Number(data))),
})

const form = useForm({
  validatorAdapter: zodValidator(),
  validators: {
    onChange: formSchema
  },
})

const onChangeAge = (handleChange) => (event: React.ChangeEvent<HTMLInputElement>) => {
  handleChange(event.target.value)
  form.setFieldValue('ageSub', event.target.value) 

  form.validateField('ageSub', 'change')  // Execute validation here.
}

return (
  <form>
      <form.Field name="age">
        {(field): React.ReactNode => {
          return (
            <Input
              type="number"
              onChange={(e): void => onChangeAge(field.handleChange)}
              value={field.state.value?.toString()}
            />
          )
        }}
    </form.Field>
    <form.Subscribe
      selector={(state): { isDisabled: boolean } => {
        return { isDisabled: state.isPristine || !state.canSubmit }
      }}
    >
      {({ isDisabled }): React.ReactNode => {
        return (
          <Button isDisabled={isDisabled} >
            submit
          </Button>
        )
      }}
    </form.Subscribe>
  </form>
)

I think the behavior change might be due to the changes introduced in https://github.com/TanStack/form/pull/925 , but I'm not certain.

mfrancis107 commented 1 month ago

Adding another example, I think this is probably the same underlying issue.

https://stackblitz.com/edit/tanstack-form-gpfjb6?file=src%2Findex.tsx

In this example either (First and Last are required) or Company Name

Steps to replicate:

Balastrong commented 5 days ago

I think I found the issue, anyone wants to give it a try on a PR?

The journey starts here:

https://github.com/TanStack/form/blob/aaeb8db4001b33fe88c70b68b555894bf397af83/packages/form-core/src/FormApi.ts#L735

If the form validator has field errors they're spread into the fields, but if there are no (more) errors, nothing happens. This means that if there are existing errors they're not removed and that's our bug.

Should be fixed on validateSync and validateAsync.

OrlovAlexei commented 2 days ago

@mfrancis107 I'm trying to correct in your example. I have a question) should all 3 errors be shown until all fields are filled in?