Removing array field item doesn't work inside of transition group, when there's a default value
Describe the bug
This bug was extremely confusing but I finally managed to reproduce it minimally.
When rendering a TransitionGroup inside of the children callback of an array form.Field, and rendering sub-fields inside of that TransitionGroup, removing any of the items re-creates the last item of the array until the array is back to the size it had in defaultValues.
Your minimal, reproducible example
https://stackblitz.com/edit/tanstack-form-c9w5c8ae?file=src%2Findex.tsx&preset=node
Steps to reproduce
- Go to the provided link
- Click on
deletenext to any of the inputs - See that the last input is re-added with the value "c"
Expected behavior
I expected the last field to be properly removed from the rendered list
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- OS: macOS
- Browser: Chrome
- Browser version: Version 138.0.7204.169 (Official Build) (arm64)
TanStack Form adapter
react-form
TanStack Form version
v1.15.0
TypeScript version
No response
Additional context
react-form's useField hook calls field.update inside of useIsomorphicLayoutEffect. Usually this isn't an issue, but when using the field inside of a TransitionGroup from react-transition-group, the effect fires when the field's state has been removed, before the element itself is properly unmounted, which causes the removed item to be re-added to the state.
This bug goes away if I don't use a nested form.Field - and manually do the logic for the nested handleChange. Is there any way to get the benefit of nested form.Fields without this issue...?
(BTW, don't know if this should be a separate issue, but the link for the "official CodeSandbox examples" in the issue template is wrong...)
https://react.dev/learn/rendering-lists#why-does-react-need-keys :)
Hey @kusiewicz - if you take a look at the example reproduction I created, you can see that I have actually used keys for the rendered items in the list, in a way that I believe shouldn't cause any issues and which is very similar to the official examples.
Here is an additional example with static IDs for the items instead of indexes, in case you think that was the problem - the issue persists. https://stackblitz.com/edit/tanstack-form-ddsbgbux?file=src%2Findex.tsx&preset=node
Probably related to #1577 ? But considering that this issue is about react-form implies it's likely a form-core issue.
Thanks for the reproducible example!
Managed to get the reproduction to be even simpler:
https://stackblitz.com/edit/tanstack-form-dysng9a7?file=src%2Findex.tsx&preset=node
I've just used a function component that does basically the same logic as what react-transition-group does
The array removal should now work with this. However, the TransitionGroup not working properly can be a side effect from how useSyncExternalStore works with transition groups, which can be broken in React v19. It's best if you install the PR version (command found in PR) to confirm whether or not it works.
Just want to add that we are also experiencing this issue. We don't use react-transition-group, but we use react-framer-motion. But we do experience that, when we have the default value being an array of multiple objects, we have some strange behaviour when we delete the last one. What is even more interesting, is that it doesn't keep all properties of the object just deleted, but just some of them.
Our workaround for now is to always initiate the form with an empty array, and then set the initial values in a useEffect after the form is initiated.
Not idea, but it works for now.
@aburto22 Could you install the #1729 version (script in PR) and let me know if the problem is fixed by it?
@aburto22 Could you install the #1729 version (script in PR) and let me know if the problem is fixed by it?
Hey! Just tried the version in #1729 and it seems to fix our issue. Good job!
@LeCarbonator installing the PR in this reproduction doesn't seem to fix the issue...
https://stackblitz.com/edit/tanstack-form-dysng9a7?file=package.json
@ej-shafran Hmm ... it might be the transition group - related issue then.
From what I know, useSyncExternalStore does not work together with React transition group because they cannot be marked as non-urgent. TanStack Store uses that hook, and TanStack Form uses TanStack Store.
The Transition Group is not only related to defaultValues. Notice how you can't use pushValue either.
@LeCarbonator
The reproduction doesn't actually use react-transition-group but instead a minimal component that creates the issue. Maybe the PR you've linked to is relevant to a different issue...?