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

UseStore array or object values causing unintended behaviour #1148

Open
harry-whorlow opened this issue Feb 11, 2025 · 4 comments
Open

UseStore array or object values causing unintended behaviour #1148

harry-whorlow opened this issue Feb 11, 2025 · 4 comments

Comments

@harry-whorlow
Copy link
Contributor

harry-whorlow commented Feb 11, 2025

Describe the bug

Calling useStore with a selector that returns an object or array field can cause weird behaviour. Results have varied from the maximum update depth, to field's un-focusing on handle change.

Interestingly form.Subscribe dose not result in these issues, so as a work around form.Subscribe can be used instead in limited cases. form.getFieldValue can also work but on first render if you're waiting for an async data fetch this can cause the default value to be rendered.

Steps to reproduce

  • create a form with a field that is of type array or object
  • create a useStore with a selector pointing to store.values

Expected behavior

Return the field variable in a manner that doesn’t break.

How often does this bug happen?

Every time / on hot reload

Screenshots or Videos

My production instance
onChange causing the field to be come unfocused. (unfortunately i cannot share more)
https://github.com/user-attachments/assets/c953fa68-13cf-4b6a-af8e-3b51efbead8e

Platform

MacOs Sequoia 15.3
Version 132.0.6834.160 (Official Build) (arm64)

TanStack Form adapter

None

TanStack Form version

0.41.3

TypeScript version

5.6.2

Additional context

I tried recreating this in a code sandbox https://codesandbox.io/p/sandbox/yv7zmh?file=%2Fsrc%2FStoreTest%2FWithStore%2Findex.tsx%3A8%2C24, but can't get the same results, but this happens every time in my work environment and also in others users environments. I'll try again to recreate this behaviour or at least another local instance in a code base I can share.

[edit] so I've recreated a simple vite app with just form at the latest version and can recreate the error, but it is a weird one. The Maximum update depth exceeded error throws only on the hot reload for the dev server, as in if I save a change and the dev server reloads the app the error throws. If I refresh the error goes away

Image

function App() {
  const form = useForm({
    defaultValues: { name: '', array: [{ hi: '' }], obj: {} },
  });

  const data = useStore(form.store, (store) => store.values);

  return (
    <Stack gap={2}>
      <> {JSON.stringify(data)}</>

      <form.Field name="array" mode="array">
        {(field) => {
          return (
            <Stack gap={0.5}>
              {field.state.value.map((_, i) => (
                <button key={i} onClick={() => field.pushValue({ hi: '' })}>
                  Add Value
                </button>
              ))}
            </Stack>
          );
        }}
      </form.Field>

      <form.Field
        name="name"
        listeners={{
          onChange: ({ value }) => {
            console.log(value);
          },
        }}>
        {({ handleChange, state }) => <input value={state.value} onChange={(e) => handleChange(e.target.value)} />}
      </form.Field>
    </Stack>
  );
}

I currently cannot recreate the un-focusing bug I have in my production server but i will continue to investigate, as it's pretty debilitating.

@harry-whorlow
Copy link
Contributor Author

@beeirl
Copy link

beeirl commented Feb 12, 2025

Here are my observations:

  • if there's only an array field, react crashes with that error, even after a refresh. (forked and extended the tanstack array example to reproduce this issue)
  • if there are additional fields (e.g., text fields alongside an array field), hot reload fails with that error, but a refresh fixes it.

@crutchcorn
Copy link
Member

Oooooooh. I sat down to debug this and I just realized why this is happening...

The TLDR is that by using a non-stable value (IE a new object or array reference on every render) we can't detect when it should stop re-rendering :/

We could introduce an ESLint plugin that prevents users from doing stuff like this, but at very least we should document that useStore should be treated more like useSelector and not return any data transforms.

@harry-whorlow @beeirl do either of you want to add this to our docs where you best see fit? (maybe even in source code so it's easily seen in auto-gen'd reference docs?)

@harry-whorlow
Copy link
Contributor Author

harry-whorlow commented Feb 21, 2025

@crutchcorn sure thing, I'll draft one now🤟

So basically this mapping here is causing infinite re-renders.
Image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants