T
TanStack2mo ago
harsh-harlequin

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
exotic-emerald
exotic-emerald2mo ago
what version of tanstack form do you use?
harsh-harlequin
harsh-harlequinOP2mo ago
@Luca | LeCarbonator was using 1.1.0, but now using 1.18.0. The result is still the same.
exotic-emerald
exotic-emerald2mo 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!
harsh-harlequin
harsh-harlequinOP2mo ago
Thank you so much @Luca | LeCarbonator ! Appreciate your help!
exotic-emerald
exotic-emerald2mo 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
exotic-emerald
exotic-emerald2mo ago
LeCarbonator
StackBlitz
TSF + Zod Sandbox (duplicated) - StackBlitz
Next generation frontend tooling. It&#39;s fast!
harsh-harlequin
harsh-harlequinOP2mo ago
Sure, let me try it out
exotic-emerald
exotic-emerald2mo ago
this error was from my part because I had installed an alpha version from a previous test
harsh-harlequin
harsh-harlequinOP2mo 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?
exotic-emerald
exotic-emerald2mo ago
maybe you tested after installing and it didn‘t reload properly
harsh-harlequin
harsh-harlequinOP2mo 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
exotic-emerald
exotic-emerald2mo ago
hmmm … can you spot a major difference between my stackblitz and your failing snippet?
harsh-harlequin
harsh-harlequinOP2mo 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
exotic-emerald
exotic-emerald2mo ago
that did it??
harsh-harlequin
harsh-harlequinOP2mo ago
using "useForm" instead of using "useAppForm"
exotic-emerald
exotic-emerald2mo ago
that … doesn‘t make any sense. But I‘ll try and confirm just in case
harsh-harlequin
harsh-harlequinOP2mo 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,
},
})
exotic-emerald
exotic-emerald2mo 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?
harsh-harlequin
harsh-harlequinOP2mo ago
yes they do
exotic-emerald
exotic-emerald2mo 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
harsh-harlequin
harsh-harlequinOP2mo 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
exotic-emerald
exotic-emerald2mo ago
well, there's of course the differences outside too. How are the defaultValues passed to the form etc.
harsh-harlequin
harsh-harlequinOP2mo ago
ok wait... this is super weird I had the following code, and when removing it from the component, it started behaving
exotic-emerald
exotic-emerald2mo ago
removing it from the component?
harsh-harlequin
harsh-harlequinOP2mo 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
exotic-emerald
exotic-emerald2mo 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)
harsh-harlequin
harsh-harlequinOP2mo ago
500 ms did it
exotic-emerald
exotic-emerald2mo ago
well, removing an internal reload from isSubmitting was how the initial fix worked to think it was this easily recreatable
harsh-harlequin
harsh-harlequinOP2mo ago
so it's like a zombie of the old issue?
exotic-emerald
exotic-emerald2mo ago
yeah
harsh-harlequin
harsh-harlequinOP2mo ago
I'm not familiar with tanstack's form code, but I took a peek and completely missed any sort of race condition
harsh-harlequin
harsh-harlequinOP2mo 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
harsh-harlequin
harsh-harlequinOP2mo ago
so, the interaction is between reset and handleSubmit I guess
exotic-emerald
exotic-emerald2mo ago
yes, it‘s specifically if reset is called in onSubmit
harsh-harlequin
harsh-harlequinOP2mo 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)
exotic-emerald
exotic-emerald2mo 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
harsh-harlequin
harsh-harlequinOP2mo 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
exotic-emerald
exotic-emerald2mo 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
harsh-harlequin
harsh-harlequinOP2mo ago
Thanks a bunch Luca
exotic-emerald
exotic-emerald2mo ago
thanks for the report!
harsh-harlequin
harsh-harlequinOP2mo 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...
robust-apricot
robust-apricot2mo ago
you don't suppose it's because the useStore is triggering a re-render?
exotic-emerald
exotic-emerald2mo ago
that's the cause, yes. You can comment it out to 'fix' the effect
robust-apricot
robust-apricot2mo ago
this is really wierd
harsh-harlequin
harsh-harlequinOP2mo ago
Yes it is

Did you find this page helpful?