[react-form] there is no way to clear a form value if it has a default value.
Describe the bug
useForm({
defaultValues: {
status: params.get('status') // assume this is active for example
}
})
<form.Field name="status" asyncAlways>
{(field) => (
....
field.setValue(undefined) // doesn't work. it resets status to active.
form.setFieldValue("status", undefined) // doesn't work. it resets status to active.
field.setValue(undefined) // doesn't work. it resets status to active.
)}
</form.Field>
they all default to the default value
Your minimal, reproducible example
TODO
Steps to reproduce
N/A
Expected behavior
I would expect that setValue / handleChange would accept undefiend and it wouldn't be overridden by the default value.
How often does this bug happen?
Every time
Screenshots or Videos
No response
Platform
- OS: Macos
- version: ^1.0.5
TanStack Form adapter
None
TanStack Form version
^1.0.5
TypeScript version
No response
Additional context
No response
Consider to use null instead of undefined.
@vitassuper I'm currently using v1.1.0 and also having this problem. In this case I do think that is is a bug, or if not, then it should probably be expressly spelled out in the docs that this is not supported. For example, I if I have a type like this:
type Exmaple = {
first: string | undefined
last: string | undefined
}
Where undefined is a valid value for one of the fields, and I set up a form with useForm:
const form = useForm({
defaultValues: { first: 'John', last: 'Smith' } as Example,
// useForm options
})
then I would think that undefined is a valid value for either field. But in the form itself, if I do something like this:
<form.Field name='first'>
<CustomInput
// Line below has unexpected behavior if value is undefined, and does not raise a type error because undefined IS a valid value
onChange(value => field.handleChange(value))
/>
</form.Field>
field.handleChange(undefined)
Instead of setting the value of that field to undefined, it will instead be reverted to 'John'. This seems like a bug to me, especially since it is typed correctly, and other form libraries that I've used in the past have not had issues with this. While agree that using null instead of undefined is probably a better choice, this conflicts with a lot of form validation that my company uses with Zod's partial() property, which is usually where the undefined values are coming from. Unfortunately Zod does not have a good way currently of making all of an object's fields null-ish (null or undefined) , so for now the partial function is the best that we can do.
Consider to use null instead of undefined.
unfortunately neither null nor undefined work.
In my case, using null does work, but it required a refactor of my validation schemas to get there.
@LastGunslinger what changes did you make, my form element is optional and should acception undefined values. ?
@a-eid I ended up changing the type of the defaultValues property in the useForm hook, from something like this:
type User {
firstName?: string | undefined
lastName?: string | undefined
}
to something like this:
type User {
firstName?: string | null | undefined
lastName?: string | null | undefined
}
Then I just made sure that all of my field.handleChange calls used null instead of undefined when called:
<CustomInput
// {...otherProps}
onChange={(value: string | null | undefined) => field.handleChange(value ?? null)}
/>
After making these changes I don't have the issue anymore, but it did require a substantial change for my project. My actual defaultValues type is not so simple, it's defined as Zod schema based on a larger type, but the idea for fixing it is the same.
This seems like a bug to me, especially since it is typed correctly, and other form libraries that I've used in the past have not had issues with this.
field.handleChanged(undefined);
field.setValue(undefined);
form.setFieldValue("AField", undefined);
form.setFieldValue("AFieldContainingAnOptionalProp", { }); // prop is set to default value, NOT omitted
The fact that all these can result in the field being set to something other than undefined seems counterintuitive and likely to lead to some ugly workarounds. (I outline one here.)
Is the rationale behind this behaviour explained anywhere?
I would agree with this, setting undefined and resetting a value to default should be separated. I ended up having to set values to null and then filter them out at the end
I agree. Unpredictable behavior. Still, this is not a reset of the value. You also have to bypass through null. Waiting for correction
I have same issue, need to make one of the values null, or undefined when changing different value. But there seems to be no way, how to set value to null.
I also notice this for arrays, and it's not clear how to fix this when removing an item in an array that has been defined in default values. For example:
useForm({
defaultValues: {
users: [
{ id: 1, name: "John" },
{ id: 2, name: "Mary" }
]
}
})
...
const users = useStore(form.store, (state) => state.values.users);
const handleDeleteUser = (index: number) => {
const user = users[index];
if (!user) return;
form.removeFieldValue("users", index); // doesn't work. The item momentarily gets removed from the list and then gets re-added.
};
https://tanstack.com/form/latest/docs/framework/react/examples/array
With a simple example, in the official docs’ Interactive Sandbox, after updating it with the code below (index.tsx):
- Delete the first Row.
- Click the “Add person” button.
You’ll see a similar issue. I expected a new row, but when it’s undefined, the existing default value is shown instead.
Example code
import * as React from 'react';
import { createRoot } from 'react-dom/client';
import { useForm } from '@tanstack/react-form';
interface Person {
name: string | undefined;
}
const defaultPeople: { people: Array<Person> } = { people: [{ name: 'gwansikk'}] };
function App() {
const form = useForm({
defaultValues: defaultPeople,
onSubmit({ value }) {
alert(JSON.stringify(value));
},
});
return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
e.stopPropagation();
form.handleSubmit();
}}
>
<form.Field name="people" mode="array">
{(field) => {
return (
<div>
{field.state.value.map((_, i) => {
return (
<form.Field key={i} name={`people[${i}].name`}>
{(subField) => {
return (
<div>
<label>
<div>Name for person {i}</div>
<input
value={subField.state.value}
onChange={(e) =>
subField.handleChange(e.target.value)
}
/>
<button
type="button"
onClick={() => {
field.removeValue(i);
}}
>
X
</button>
</label>
</div>
);
}}
</form.Field>
);
})}
<button
onClick={() => field.pushValue({ name: undefined })}
type="button"
>
Add person
</button>
</div>
);
}}
</form.Field>
<form.Subscribe
selector={(state) => [state.canSubmit, state.isSubmitting]}
children={([canSubmit, isSubmitting]) => (
<button type="submit" disabled={!canSubmit}>
{isSubmitting ? '...' : 'Submit'}
</button>
)}
/>
</form>
</div>
);
}
const rootElement = document.getElementById('root')!;
createRoot(rootElement).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
I'm also running into this issue. I need to clear a form field, but setting undefined causes the form's initial value to reappear instead.
I've also resorted to using null, but it's creating a headache with Zod elsewhere in my codebase, where I need to replace optional() with nullish() in many places, which is undesirable and causes Typescript errors to ripple through other areas that aren't expecting null as a type.
An alternative is to strip null fields manually, but that's another headache of hunting down all the places where form state is extracted and used elsewhere, and is a constant game of whack-a-mole.
Resetting vs. setting a value of undefined should be separate and distinct actions, IMO.
I'm also running into this issue. I need to clear a form field, but setting
undefinedcauses the form's initial value to reappear instead.I've also resorted to using
null, but it's creating a headache with Zod elsewhere in my codebase, where I need to replaceoptional()withnullish()in many places, which is undesirable and causes Typescript errors to ripple through other areas that aren't expectingnullas a type.An alternative is to strip
nullfields manually, but that's another headache of hunting down all the places where form state is extracted and used elsewhere, and is a constant game of whack-a-mole.Resetting vs. setting a value of undefined should be separate and distinct actions, IMO.
I agree. I want to contribute to resolving this. It occurs not only in React Form but also in other library adapters.
I'm also having issues with this. My current hacky workaround is to set all default values to null, then use setFieldValue on mount. This way, I can call resetField to clear both the state and any errors.