React warning when setting defaultValue on conditionally rendered Field.
Describe the bug
React crashes if you set a Field level defaultValue on a conditionally rendered Field.
Cannot update a component (`App`) while rendering a different component (`Field`). To locate the bad setState() call inside `Field`, follow the stack trace as described in https://react.dev/link/setstate-in-render
at Field (https://tkj4dz.csb.app/node_modules/@tanstack/react-form/dist/esm/useField.js:43:23)
at APIField
at div
at form
at div
at App
Your minimal, reproducible example
https://codesandbox.io/p/sandbox/optimistic-golick-tkj4dz
Steps to reproduce
- Type something in First name so last name gets rendered
Expected behavior
Should not crash
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- OS: Windows 10
TanStack Form adapter
None
TanStack Form version
v0.41.3
TypeScript version
No response
Additional context
No response
This doesn't seem to crash for me, but agreed we should look into fixing at some point. Help from the community would be appreciated here.
I just went back and you're absolutely right it doesn't 'crash' not sure why I described it that way, my apologies.
Pretty sure this is caused by us calling setFieldValue directly on the constructor of FieldApi here: https://github.com/TanStack/form/blob/72b4572ed644f6699eacb186fdd6c2f536c73219/packages/form-core/src/FieldApi.ts#L999-L1002 When you defined a defaultValue, and the component is rendering, within the same cycle we're attempting to update state which seems to violate a React rule about this. You'll notice in your stackblitz if you comment out defaultValue on lastName issue goes away.
Typically you'd end up having a useEffect that runs once the component is mounted, but this won't work for us since this is framework agnostic. I'll take a crack at this after #1201!
Update: a simple workaround right now is to not conditionally render the whole form.Field, but rather just content within the children. This way we ensure the Field is initiated despite the DOM not showing anything.
Example:
<form.Field
name="lastName"
defaultValue="Smith"
validators={{
onChange: ({ value }) =>
!value
? "A last name name is required"
: value.length < 3
? "last name must be at least 3 characters"
: undefined,
}}
children={
firstName // if firstName not falsy render underlying component else null
? (field) => (
<>
<label htmlFor={field.name}>Last Name:</label>
<input
id={field.name}
name={field.name}
value={field.state.value}
onBlur={field.handleBlur}
onChange={(e) => field.handleChange(e.target.value)}
/>
<FieldInfo field={field} />
</>
)
: null
}
/>
@asegarra has this been resolved for you in latest?