T
TanStack4mo ago
extended-salmon

Reset form after Submit not working as expected?

So, I have a React App and a simple form and I want to set the default values to whatever the form has at the time of submission. However, doing so resets the form to the original defaultValues, instead of the new ones passed to the function. Is there anything I'm doing wrong? Here's a snippet with this:
import { Button } from '@/components/ui/button';
import { useAppForm } from '@/hooks/form';
import { useStore } from '@tanstack/react-form';
import { z } from 'zod';

const formSchema2 = z.object({
gender: z.boolean(),
});

export function TestForm() {
const form = useAppForm({
defaultValues: {
gender: true,
},
validators: {
onChange: formSchema2,
},
onSubmit: async (params) => {
// params.value is correct here. If the toggle is false, it shows params.value.gender is false
console.log(params.value);

params.formApi.reset(params.value);
// after this line, the form goes back to the original defaultValues ({gender: true}) instead of the new one ({gender:false})
},
});

return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<form.AppForm>
<form.AppField
name='gender'
children={(field) => <field.ToggleField name={field.name} label='Show on Resume' className='ml-2' />}
/>
<Button type='button' onClick={() => form.handleSubmit()} disabled={!isDirty} className='mt-4'>
Submit
</Button>
</form.AppForm>
</form>
</div>
);
}
import { Button } from '@/components/ui/button';
import { useAppForm } from '@/hooks/form';
import { useStore } from '@tanstack/react-form';
import { z } from 'zod';

const formSchema2 = z.object({
gender: z.boolean(),
});

export function TestForm() {
const form = useAppForm({
defaultValues: {
gender: true,
},
validators: {
onChange: formSchema2,
},
onSubmit: async (params) => {
// params.value is correct here. If the toggle is false, it shows params.value.gender is false
console.log(params.value);

params.formApi.reset(params.value);
// after this line, the form goes back to the original defaultValues ({gender: true}) instead of the new one ({gender:false})
},
});

return (
<div>
<form
onSubmit={(e) => {
e.preventDefault();
form.handleSubmit();
}}
>
<form.AppForm>
<form.AppField
name='gender'
children={(field) => <field.ToggleField name={field.name} label='Show on Resume' className='ml-2' />}
/>
<Button type='button' onClick={() => form.handleSubmit()} disabled={!isDirty} className='mt-4'>
Submit
</Button>
</form.AppForm>
</form>
</div>
);
}
45 Replies
wise-white
wise-white4mo ago
what version of tanstack form do you use?
extended-salmon
extended-salmonOP4mo ago
@Luca | LeCarbonator was using 1.1.0, but now using 1.18.0. The result is still the same.
wise-white
wise-white4mo ago
hmm, strange. Bad news, I see the same error you do. Good news, I can replicate it. I‘ll check what the issue may be and get back to you 👍 thanks for the report!
extended-salmon
extended-salmonOP4mo ago
Thank you so much @Luca | LeCarbonator ! Appreciate your help!
wise-white
wise-white4mo ago
can you confirm that it's truly not been fixed? I don't remember which minor version it was (somewhere between .10 and .17), but the reset in onSubmit has had a fix
wise-white
wise-white4mo ago
LeCarbonator
StackBlitz
TSF + Zod Sandbox (duplicated) - StackBlitz
Next generation frontend tooling. It&#39;s fast!
extended-salmon
extended-salmonOP4mo ago
Sure, let me try it out
wise-white
wise-white4mo ago
this error was from my part because I had installed an alpha version from a previous test
extended-salmon
extended-salmonOP4mo ago
oh I see. I did have a previous version installed in this project. and using pnpm as package manager. maybe something got mixed up?
wise-white
wise-white4mo ago
maybe you tested after installing and it didn‘t reload properly
extended-salmon
extended-salmonOP4mo ago
That could be true. And I've just tested it, but no luck Let me remove and reinstall the package Still nothing I also just reviewed my pnpm-lock.yaml and looks like the right version is referenced everywhere also, looks like the code for v 1.18.0 is the one in my node_modules
wise-white
wise-white4mo ago
hmmm … can you spot a major difference between my stackblitz and your failing snippet?
extended-salmon
extended-salmonOP4mo ago
I think one of the biggest differences is that I use "useAppForm" instead of plain "useForm", because I have a bunch of custom components Let me try your exact example in my project yep... that did it
wise-white
wise-white4mo ago
that did it??
extended-salmon
extended-salmonOP4mo ago
using "useForm" instead of using "useAppForm"
wise-white
wise-white4mo ago
that … doesn‘t make any sense. But I‘ll try and confirm just in case
extended-salmon
extended-salmonOP4mo ago
Just FYI, this is what I'm doing in order to generate the useAppForm hook (pretty much textbook example from the docs):
import { lazy } from 'react'
import { createFormHook } from '@tanstack/react-form'
import { fieldContext, formContext } from './form-context'

const TextField = lazy(() => import('../components/text-field.tsx'))
const NumberField = lazy(() => import('../components/number-field.tsx'))
const SelectField = lazy(() => import('../components/select-field.tsx'))
const DatePickerField = lazy(() => import('../components/date-picker-field.tsx'))
const RadioGroupField = lazy(() => import('../components/radio-group-field.tsx'))
const CheckboxField = lazy(() => import('../components/checkbox-field.tsx'))
const ToggleField = lazy(() => import('../components/toggle-field.tsx'))
const SubmitButton = lazy(() => import('../components/submit-button.tsx'))

export const { useAppForm, withForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: {
TextField,
NumberField,
SelectField,
DatePickerField,
RadioGroupField,
CheckboxField,
ToggleField,
},
formComponents: {
SubmitButton,
},
})
import { lazy } from 'react'
import { createFormHook } from '@tanstack/react-form'
import { fieldContext, formContext } from './form-context'

const TextField = lazy(() => import('../components/text-field.tsx'))
const NumberField = lazy(() => import('../components/number-field.tsx'))
const SelectField = lazy(() => import('../components/select-field.tsx'))
const DatePickerField = lazy(() => import('../components/date-picker-field.tsx'))
const RadioGroupField = lazy(() => import('../components/radio-group-field.tsx'))
const CheckboxField = lazy(() => import('../components/checkbox-field.tsx'))
const ToggleField = lazy(() => import('../components/toggle-field.tsx'))
const SubmitButton = lazy(() => import('../components/submit-button.tsx'))

export const { useAppForm, withForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: {
TextField,
NumberField,
SelectField,
DatePickerField,
RadioGroupField,
CheckboxField,
ToggleField,
},
formComponents: {
SubmitButton,
},
})
wise-white
wise-white4mo ago
yeah, doesn't seem like a problem what happens if you add this snippet
<form.Subscribe selector={(state) => state.values}>
{(values) => <pre>{JSON.stringify(values, null, 3)}</pre>}
</form.Subscribe>
<form.Subscribe selector={(state) => state.values}>
{(values) => <pre>{JSON.stringify(values, null, 3)}</pre>}
</form.Subscribe>
see what the values are reactively. Do they also wrongly update?
extended-salmon
extended-salmonOP4mo ago
yes they do
wise-white
wise-white4mo ago
https://stackblitz.com/edit/vitejs-vite-jd2ttqqx?file=src%2FApp.tsx here's a version with useAppForm. Perhaps the field component is the key? because this appears to work the same
extended-salmon
extended-salmonOP4mo ago
testing! I'm sorry to be such a troublemaker here, but the same thing just happened. Instead of passing the field.ToggleField component, I used the same plain html from your example and didn't work I'm reading yours line by line trying to figure out if there are other differences
wise-white
wise-white4mo ago
well, there's of course the differences outside too. How are the defaultValues passed to the form etc.
extended-salmon
extended-salmonOP4mo ago
ok wait... this is super weird I had the following code, and when removing it from the component, it started behaving
wise-white
wise-white4mo ago
removing it from the component?
extended-salmon
extended-salmonOP4mo ago
const [isDirty] = useStore(form.store, (state) => [state.isDirty]);
const [isDirty] = useStore(form.store, (state) => [state.isDirty]);
That's the code that I use to track whether or not the submit button should be enabled. when I remove it, the form's reset behaves
wise-white
wise-white4mo ago
that sounds like it could be it what's likely happening is a race condition between two state changes: one change to set the new defaultValues, the other to set isSubmitting and isDirty etc. to their old values if the defaultValues is done first, then the isSubmitting and isDirty overwrites it accidentally should be easy to check though. Does this fix your implementation:
- params.formApi.reset(params.value)
+ setTimeout(params.formApi.reset(params.value), 50)
- params.formApi.reset(params.value)
+ setTimeout(params.formApi.reset(params.value), 50)
extended-salmon
extended-salmonOP4mo ago
500 ms did it
wise-white
wise-white4mo ago
well, removing an internal reload from isSubmitting was how the initial fix worked to think it was this easily recreatable
extended-salmon
extended-salmonOP4mo ago
so it's like a zombie of the old issue?
wise-white
wise-white4mo ago
yeah
extended-salmon
extended-salmonOP4mo ago
I'm not familiar with tanstack's form code, but I took a peek and completely missed any sort of race condition
extended-salmon
extended-salmonOP4mo ago
GitHub
form/packages/form-core/src/FormApi.ts at main · TanStack/form
🤖 Headless, performant, and type-safe form state management for TS/JS, React, Vue, Angular, Solid, and Lit. - TanStack/form
extended-salmon
extended-salmonOP4mo ago
so, the interaction is between reset and handleSubmit I guess
wise-white
wise-white4mo ago
yes, it‘s specifically if reset is called in onSubmit
extended-salmon
extended-salmonOP4mo ago
ouch. and I would guess there's no other place to hook into and do the reset, like an "onAfterSubmit" (FML with that name)
wise-white
wise-white4mo ago
no, but you ought not to since this is "only" needed if you use a store hook of a property that is affected by the end of submissions so I suppose the fix wasn't as simple as we initially thought. @nneaowwplane good luck :KEKPOINT: could you open a github issue for this? @Rodrigo
extended-salmon
extended-salmonOP4mo ago
On it @Luca | LeCarbonator , and thank you a lot for your help on this one. @nneaowwplane I guess you're the one who's taking it on... oh boy I'm so sorry
wise-white
wise-white4mo ago
he's the one who was forced to tackle the previous one :mhm: https://stackblitz.com/edit/vitejs-vite-jd2ttqqx?file=src%2FApp.tsx here's a reproduction for the issue
extended-salmon
extended-salmonOP4mo ago
Thanks a bunch Luca
wise-white
wise-white4mo ago
thanks for the report!
extended-salmon
extended-salmonOP4mo ago
GitHub
form.reset during onSubmit ignores new default values · Issue #168...
Describe the bug The issue is that if you use the form&#39;s reset function from inside the form&#39;s onSubmit with new default values, they will be ignored. This only happens if used in combinati...
unwilling-turquoise
unwilling-turquoise4mo ago
you don't suppose it's because the useStore is triggering a re-render?
wise-white
wise-white4mo ago
that's the cause, yes. You can comment it out to 'fix' the effect
unwilling-turquoise
unwilling-turquoise4mo ago
this is really wierd
extended-salmon
extended-salmonOP4mo ago
Yes it is

Did you find this page helpful?