Skip to content

Commit 6510d8b

Browse files
fix(react): form.reset not working inside of onSubmit (#1494)
1 parent 824d723 commit 6510d8b

File tree

5 files changed

+182
-3
lines changed

5 files changed

+182
-3
lines changed

packages/angular-form/tests/tanstack-field.spec.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,3 +370,52 @@ describe('TanStackFieldDirective', () => {
370370
expect(getByText(onBlurError)).toBeInTheDocument()
371371
})
372372
})
373+
374+
describe('form should reset default value when resetting in onSubmit', () => {
375+
it('should be able to handle async resets', async () => {
376+
@Component({
377+
selector: 'test-component',
378+
standalone: true,
379+
template: `
380+
<ng-container [tanstackField]="form" name="name" #f="field">
381+
<input
382+
data-testid="fieldinput"
383+
[value]="f.api.state.value"
384+
(input)="f.api.handleChange($any($event).target.value)"
385+
/>
386+
</ng-container>
387+
<button
388+
type="button"
389+
(click)="form.handleSubmit()"
390+
data-testid="submit"
391+
>
392+
submit
393+
</button>
394+
`,
395+
imports: [TanStackField],
396+
})
397+
class TestComponent {
398+
form = injectForm({
399+
defaultValues: {
400+
name: '',
401+
},
402+
onSubmit: ({ value }) => {
403+
expect(value).toEqual({ name: 'test' })
404+
this.form.reset({ name: 'test' })
405+
},
406+
})
407+
}
408+
409+
const { getByTestId } = await render(TestComponent)
410+
411+
const input = getByTestId('fieldinput')
412+
const submit = getByTestId('submit')
413+
414+
await user.type(input, 'test')
415+
await expect(input).toHaveValue('test')
416+
417+
await user.click(submit)
418+
419+
await expect(input).toHaveValue('test')
420+
})
421+
})

packages/form-core/tests/FormApi.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,25 @@ describe('form api', () => {
120120
})
121121
})
122122

123+
it('form should reset default value when resetting in onSubmit', async () => {
124+
const defaultValues = {
125+
name: '',
126+
}
127+
const form = new FormApi({
128+
defaultValues: defaultValues,
129+
onSubmit: ({ value }) => {
130+
form.reset(value)
131+
132+
expect(form.options.defaultValues).toMatchObject({
133+
name: 'test',
134+
})
135+
},
136+
})
137+
form.mount()
138+
form.setFieldValue('name', 'test')
139+
form.handleSubmit()
140+
})
141+
123142
it('should reset and set the new default values that are restored after an empty reset', () => {
124143
const form = new FormApi({ defaultValues: { name: 'initial' } })
125144
form.mount()

packages/react-form/src/useForm.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { FormApi, functionalUpdate } from '@tanstack/form-core'
22
import { useStore } from '@tanstack/react-store'
3-
import React, { useState } from 'react'
3+
import { useState } from 'react'
44
import { Field } from './useField'
55
import { useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect'
66
import type {
@@ -190,9 +190,11 @@ export function useForm<
190190
TOnServer,
191191
TSubmitMeta
192192
> = api as never
193+
193194
extendedApi.Field = function APIField(props) {
194195
return <Field {...props} form={api} />
195196
}
197+
196198
extendedApi.Subscribe = function Subscribe(props: any) {
197199
return (
198200
<LocalSubscribe
@@ -208,8 +210,6 @@ export function useForm<
208210

209211
useIsomorphicLayoutEffect(formApi.mount, [])
210212

211-
useStore(formApi.store, (state) => state.isSubmitting)
212-
213213
/**
214214
* formApi.update should not have any side effects. Think of it like a `useRef`
215215
* that we need to keep updated every render with the most up-to-date information.

packages/react-form/tests/useForm.test.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,4 +794,64 @@ describe('useForm', () => {
794794

795795
expect(fn).toHaveBeenCalledTimes(1)
796796
})
797+
798+
it('form should reset default value when resetting in onSubmit', async () => {
799+
function Comp() {
800+
const form = useForm({
801+
defaultValues: {
802+
name: '',
803+
},
804+
onSubmit: ({ value }) => {
805+
expect(value).toEqual({ name: 'another-test' })
806+
807+
form.reset(value)
808+
},
809+
})
810+
811+
return (
812+
<form
813+
onSubmit={(e) => {
814+
e.preventDefault()
815+
e.stopPropagation()
816+
form.handleSubmit()
817+
}}
818+
>
819+
<form.Field
820+
name="name"
821+
children={(field) => (
822+
<input
823+
data-testid="fieldinput"
824+
name={field.name}
825+
value={field.state.value}
826+
onChange={(e) => field.handleChange(e.target.value)}
827+
/>
828+
)}
829+
/>
830+
831+
<button type="submit" data-testid="submit">
832+
submit
833+
</button>
834+
835+
<button type="reset" data-testid="reset" onClick={() => form.reset()}>
836+
Reset
837+
</button>
838+
</form>
839+
)
840+
}
841+
842+
const { getByTestId } = render(<Comp />)
843+
const input = getByTestId('fieldinput')
844+
const submit = getByTestId('submit')
845+
const reset = getByTestId('reset')
846+
847+
await user.type(input, 'test')
848+
await waitFor(() => expect(input).toHaveValue('test'))
849+
850+
await user.click(reset)
851+
await waitFor(() => expect(input).toHaveValue(''))
852+
853+
await user.type(input, 'another-test')
854+
await user.click(submit)
855+
await waitFor(() => expect(input).toHaveValue('another-test'))
856+
})
797857
})

packages/vue-form/tests/useForm.test.tsx

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,4 +476,55 @@ describe('useForm', () => {
476476
await waitFor(() => getByText(error))
477477
expect(getByText(error)).toBeInTheDocument()
478478
})
479+
480+
it('form should reset default value when resetting in onSubmit', async () => {
481+
const Comp = defineComponent(() => {
482+
const form = useForm({
483+
defaultValues: {
484+
name: '',
485+
},
486+
onSubmit: ({ value }) => {
487+
expect(value).toEqual({ name: 'test' })
488+
489+
form.reset({ name: 'test' })
490+
},
491+
})
492+
493+
return () => (
494+
<div>
495+
<form.Field name="name">
496+
{({ field }: { field: AnyFieldApi }) => (
497+
<input
498+
data-testid="fieldinput"
499+
name={field.name}
500+
value={field.state.value}
501+
onInput={(e) =>
502+
field.handleChange((e.target as HTMLInputElement).value)
503+
}
504+
/>
505+
)}
506+
</form.Field>
507+
508+
<button
509+
type="button"
510+
onClick={() => form.handleSubmit()}
511+
data-testid="submit"
512+
>
513+
submit
514+
</button>
515+
</div>
516+
)
517+
})
518+
519+
const { getByTestId } = render(<Comp />)
520+
const input = getByTestId('fieldinput')
521+
const submit = getByTestId('submit')
522+
523+
await user.type(input, 'test')
524+
await waitFor(() => expect(input).toHaveValue('test'))
525+
526+
await user.click(submit)
527+
528+
await waitFor(() => expect(input).toHaveValue('test'))
529+
})
479530
})

0 commit comments

Comments
 (0)