Describe the bug
Hey! This is the same symptom as #1518 / #1363 — array items keyed by uuid instead of index, remove one that isn't the last, and fields in the surviving items render once with state.value === undefined before snapping back. If the render prop does anything strict with the value (an invariant, Object.entries(value), an in check, a controlled input), that one render pass blows up — even though form state itself is fine the whole time.
Here's the fun part though: this was actually fixed by #1729 in 1.26.0, and then regressed a week later. I bisected releases with the same repro:
| react-form version |
result |
| ≤ 1.25.x |
broken (original #1518 bug) |
| 1.26.0 |
fixed ✅ |
| 1.27.0 |
broken again |
| 1.33.1 (latest I tested) |
still broken |
The regressing commit is 8afbfc39 (#1893, "fix: react compiler should now work in all edgecases", shipped in 1.27.0).
What I think is happening
#1729 fixed this in useField by recreating the FieldApi synchronously when the name changes:
const fieldApi = useMemo(() => new FieldApi({ ... }), [opts.form, opts.name])
#1893 swapped that for a setState-during-render pattern:
if (prevOptions.form !== opts.form || prevOptions.name !== opts.name) {
setFieldApi(new FieldApi({ ...opts }))
setPrevOptions({ form: opts.form, name: opts.name })
}
My hunche: when you setState during render, React discards the pass and re-renders. Right?? lol. But the current invocation still runs to completion with the old state. So the render prop executes exactly once with the new name prop and the old FieldApi, whose store now derives undefined for the path that no longer exists.
I tested this on 1.33.1 after removing item 0 of 2:
name prop: sections[0].sectionTitle ← new, correct
field.name (FieldApi instance): sections[1].sectionTitle ← stale
field.state.value: undefined
form.getFieldValue(nameProp): "Section Content 2" ← form state is fine!
So the form knows the value (getFieldValue), but the field is looking it up under stale state and finds nothing. People on #1518 spotted this exact mismatch.
Your minimal, reproducible example
https://stackblitz.com/edit/tanstack-form-y5yg6fxf?file=src%2Fapp.tsx
Steps to reproduce
Two forks of the same StackBlitz, only the pinned version differs (both log the installed version to the console on load):
Failing on 1.33.1: StackBlitz w/1.33.1
Passing on 1.26.0: StackBlitz w/1.26.0
Steps: click Add Section twice, open the console, click the first Remove Section. On 1.33.1 you get a transient undefined! console.error with the fingerprint above; on 1.26.0, its all guud.
The repro's render code is deliberately tolerant (value ?? '') so the console log is the evidence but any strict render code turns this into a hard crash. Remove the ?? '' and you get React's controlled→uncontrolled warning (#1363's symptom). In my actual app (config-driven dynamic fields doing fieldName in field.state.value), this surfaces as an unhandled render error: Cannot use 'in' operator to search for '...' in undefined. The original #1518 repro with invariant shows the crashing form too.
Expected behavior
As a user, I expected removing an item from a uuid-keyed array field to remove just that item, with the remaining fields continuing to show their values. Instead, fields in the items after the removed one briefly render with state.value === undefined even though the value is still in form state.
A field should report the value at the path it was asked to render. In the bad pass, the path has a value (getFieldValue returns it) but the field reads undefined. I believe this is because for that one render, the name prop and field.name point at two different array indexes. On 1.26.0 this never happens. Happy to re-run my repro against any candidate fix.
Bonus footgun I hit while looking for workarounds
Adding a defaultValue prop to the affected field looks like a fix (no more undefined) but actually loses data on 1.27+: FieldApi.mount() overwrites an untouched field's existing form value with defaultValue, so any keyed remount clobbers values that were already pushed/loaded into form state. Flagging it so nobody grabs that as the workaround. (The workaround that does hold up: keying the inner form.Field by its full name path so it remounts with the correct name.)
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- OS: any (repro'd on Linux + jsdom and in-browser)
- React: 19.x
- @tanstack/react-form: fails 1.27.0–1.33.1, passes on 1.26.0
TanStack Form adapter
@tanstack/react-form
TanStack Form version
1.33.1
TypeScript version
No response
Additional context
No response
Describe the bug
Hey! This is the same symptom as #1518 / #1363 — array items keyed by uuid instead of index, remove one that isn't the last, and fields in the surviving items render once with
state.value === undefinedbefore snapping back. If the render prop does anything strict with the value (an invariant,Object.entries(value), anincheck, a controlled input), that one render pass blows up — even though form state itself is fine the whole time.Here's the fun part though: this was actually fixed by #1729 in 1.26.0, and then regressed a week later. I bisected releases with the same repro:
The regressing commit is
8afbfc39(#1893, "fix: react compiler should now work in all edgecases", shipped in 1.27.0).What I think is happening
#1729 fixed this in
useFieldby recreating the FieldApi synchronously when the name changes:#1893 swapped that for a setState-during-render pattern:
My hunche: when you
setStateduring render, React discards the pass and re-renders. Right?? lol. But the current invocation still runs to completion with the old state. So the render prop executes exactly once with the newnameprop and the old FieldApi, whose store now derivesundefinedfor the path that no longer exists.I tested this on 1.33.1 after removing item 0 of 2:
So the form knows the value (
getFieldValue), but the field is looking it up under stale state and finds nothing. People on #1518 spotted this exact mismatch.Your minimal, reproducible example
https://stackblitz.com/edit/tanstack-form-y5yg6fxf?file=src%2Fapp.tsx
Steps to reproduce
Two forks of the same StackBlitz, only the pinned version differs (both log the installed version to the console on load):
Failing on 1.33.1: StackBlitz w/1.33.1
Passing on 1.26.0: StackBlitz w/1.26.0
Steps: click Add Section twice, open the console, click the first Remove Section. On 1.33.1 you get a transient undefined! console.error with the fingerprint above; on 1.26.0, its all guud.
The repro's render code is deliberately tolerant (value ?? '') so the console log is the evidence but any strict render code turns this into a hard crash. Remove the ?? '' and you get React's controlled→uncontrolled warning (#1363's symptom). In my actual app (config-driven dynamic fields doing fieldName in field.state.value), this surfaces as an unhandled render
error: Cannot use 'in' operator to search for '...' in undefined. The original #1518 repro with invariant shows the crashing form too.Expected behavior
As a user, I expected removing an item from a uuid-keyed array field to remove just that item, with the remaining fields continuing to show their values. Instead, fields in the items after the removed one briefly render with
state.value === undefinedeven though the value is still in form state.A field should report the value at the path it was asked to render. In the bad pass, the path has a value (
getFieldValuereturns it) but the field reads undefined. I believe this is because for that one render, thenameprop andfield.namepoint at two different array indexes. On 1.26.0 this never happens. Happy to re-run my repro against any candidate fix.Bonus footgun I hit while looking for workarounds
Adding a
defaultValueprop to the affected field looks like a fix (no more undefined) but actually loses data on 1.27+:FieldApi.mount()overwrites an untouched field's existing form value withdefaultValue, so any keyed remount clobbers values that were already pushed/loaded into form state. Flagging it so nobody grabs that as the workaround. (The workaround that does hold up: keying the innerform.Fieldby its full name path so it remounts with the correct name.)How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
TanStack Form adapter
@tanstack/react-form
TanStack Form version
1.33.1
TypeScript version
No response
Additional context
No response