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

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

Open
zaosoula opened this issue Jul 10, 2024 · 7 comments
Open

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

zaosoula opened this issue Jul 10, 2024 · 7 comments

Comments

@zaosoula
Copy link

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
Copy link
Author

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
Copy link
Author

Changes relative to this had been added by this commit: 4e4a3ae
(poke @crutchcorn)

@zaosoula
Copy link
Author

zaosoula commented Jul 10, 2024

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
Copy link
Member

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
Copy link

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
Copy link

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?

@jacekwilczynski
Copy link

@crutchcorn , I have a sandbox reproducing the issue:
https://codesandbox.io/p/sandbox/qkt8f4?file=%2Fsrc%2FApp.tsx

  1. Click "Set extra".
  2. Click "Show extra".
  3. Click "Set extra" again.

It's caused by the fact that setFieldValue only adds meta via spread: https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1067. However, if the field has not been initialised, e.g. with the Field component or the useField hook, prev is undefined, and therefore the resulting meta is incomplete.

I thought whether this is a bug or a result of library misuse, but I'm leaning towards the first, because:

  • I believe that in a complex & dynamic form, it's normal to have conditionally rendered fields and array fields, as well as subscribers that depend on certain values, where the order of component render vs. subscriber execution is not always guaranteed,
  • it's currently possible to use the library's public API to put its data in a state which doesn't conform with its own type declarations.

Perhaps we should add a check if prev is undefined, and if so, initialise it?

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

5 participants