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.7k stars 333 forks source link

errorMap is undefined in field's meta but the lib try to access it #845

Open zaosoula opened 2 months ago

zaosoula commented 2 months ago

Sometimes when updating field i get error because the lib try to read "onServer"/"onChange" inside "errorMap" but "errorMap" is not defined

Screenshot 2024-07-10 at 08 52 36 Screenshot 2024-07-10 at 08 53 39

tanstack/form-core: 0.26.3 tanstack/vue-form: 0.26.3

To prevent the error I added this snippet after calling "useForm"

  const store = form.useStore();
  Object.entries(store.value.values).forEach(([key, value]) => {
    form.setFieldMeta(key, (prev) => ({
      errorMap: {},
      ...prev,
    }));
  })
zaosoula commented 2 months ago

Additional context: it happens after calling form.setFieldValue directly after useForm

watch(deps, () => {
  form.setFieldValue('name', 'john doe')
})

but if i do this, it work

watch(deps, () => {
    nextTick(() => {
    form.setFieldValue('name', 'john doe')
  }
})

however calling setFieldValue with { dontUpdateMeta: true } prevent the bug from happening

watch(deps, () => {
  form.setFieldValue('name', 'john doe', { dontUpdateMeta: true })
})
zaosoula commented 2 months ago

Changes relative to this had been added by this commit: https://github.com/TanStack/form/commit/4e4a3ae12c3f2b3458ab1bd5f1beaf161f7b6995 (poke @crutchcorn)

zaosoula commented 2 months ago

I'll leave there the details of my debugging for context purpose, my final fixes was to remove the snippet mentioned above

  const store = form.useStore();
  Object.entries(store.value.values).forEach(([key, value]) => {
    form.setFieldMeta(key, (prev) => ({
      errorMap: {},
      ...prev,
    }));
  })

and always call setFieldValue with opts: { dontUpdateMeta: true }

(which is not documented and not mentioned in the releases notes)

crutchcorn commented 2 months ago

If you are using dontUpdateMeta you are almost 1000000% doing something wrong. There is a good reason it is not documented.

Please follow the issue template and provide a minimal reproduction, otherwise there's nothing we can do to help.

BrendonSled commented 1 month ago

Might be a little late to the party but I ran into the same issue. It ended up being an onChange firing after the component unmounted. I ended up wrapping the onChange with a conditional check if a ref to that component was not null.

Like so:


onChange={(markdown) => {
    if (mdxEditorRef.current) {
        onChange(markdown);
    }
}}
craigbehnke commented 3 weeks ago

I also ran into an error that I think is related to this issue. I also was using setState and setMeta like this:

field.form.store.setState(x => ({
    ...x,
    values: {
      myValue: true,
      myOtherValue: 12345
    }
}))
field.setMeta(x => ({
    ...x,
    isTouched: true
}))

While I am not sure that the above snippet is responsible, I was getting the following error stacktrace when I tried to reset the form (form.reset()):

Uncaught TypeError: Cannot convert undefined or null to object
    at Function.values (<anonymous>)
    at Object.onUpdate (FieldApi.ts:470:1)
    at Store.setState (index.ts:48:1)
    at FieldApi.ts:528:1
    at Store.batch (index.ts:64:1)
    at FieldApi.ts:523:1
    at index.ts:59:1
    at Set.forEach (<anonymous>)
    at Store._flush (index.ts:57:1)
    at Store.batch (index.ts:68:1)Caused by: React ErrorBoundary TypeError: Cannot convert undefined or null to object

I was able to work around the issue by adding the following onMount callback to my field validator object, like so:

<form.Field
  name="myValue"
  validators={{
    onMount: field => {
      if (
        !field.fieldApi.state.meta.errorMap
      ) {
        field.fieldApi.state.meta.errorMap =
          {}
      }
      return null
    }
  }}
>{/* Field display components */}</form.Field>

Any idea what could be causing this?