Skip to content

Commit 1d7dbe6

Browse files
committed
fix(form-core): keep onSubmit error when blurring an unchanged field
The submit-error auto-clear in FieldApi.validateSync fired on every non-submit validation cause, including blur. As a result, focusing and leaving a field without editing it dropped a valid submit error. Gate the clear on a value `change` cause so the error is only removed when the user actually enters a new value, matching the documented intent of the block. Closes #1242
1 parent 6a73479 commit 1d7dbe6

2 files changed

Lines changed: 63 additions & 2 deletions

File tree

packages/form-core/src/FieldApi.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,14 +1347,17 @@ export class FieldApi<
13471347

13481348
/**
13491349
* when we have an error for onSubmit in the state, we want
1350-
* to clear the error as soon as the user enters a valid value in the field
1350+
* to clear the error as soon as the user enters a valid value in the field.
1351+
* This must only happen on a value `change` - clearing it on `blur` (or any
1352+
* other non-value cause) would wrongly drop the submit error when the user
1353+
* focuses and leaves the field without editing it.
13511354
*/
13521355
const submitErrKey = getErrorMapKey('submit')
13531356

13541357
if (
13551358
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
13561359
this.state.meta.errorMap?.[submitErrKey] &&
1357-
cause !== 'submit' &&
1360+
cause === 'change' &&
13581361
!hasErrored
13591362
) {
13601363
this.setMeta((prev) => ({

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2098,6 +2098,64 @@ describe('field api', () => {
20982098
expect(field.getMeta().errors).toStrictEqual(['first name is required'])
20992099
})
21002100

2101+
it('should not clear onSubmit errors on blur when the value did not change', async () => {
2102+
const form = new FormApi({
2103+
defaultValues: {
2104+
firstName: '',
2105+
},
2106+
})
2107+
2108+
form.mount()
2109+
2110+
const field = new FieldApi({
2111+
form,
2112+
name: 'firstName',
2113+
validators: {
2114+
onSubmit: ({ value }) =>
2115+
value.length > 0 ? undefined : 'first name is required',
2116+
},
2117+
})
2118+
2119+
field.mount()
2120+
2121+
await form.handleSubmit()
2122+
expect(field.getMeta().errors).toStrictEqual(['first name is required'])
2123+
2124+
// Blurring the field without changing its value must keep the submit error
2125+
field.handleBlur()
2126+
expect(field.getMeta().errors).toStrictEqual(['first name is required'])
2127+
expect(field.getMeta().isValid).toBe(false)
2128+
})
2129+
2130+
it('should clear onSubmit errors once a valid value is entered', async () => {
2131+
const form = new FormApi({
2132+
defaultValues: {
2133+
firstName: '',
2134+
},
2135+
})
2136+
2137+
form.mount()
2138+
2139+
const field = new FieldApi({
2140+
form,
2141+
name: 'firstName',
2142+
validators: {
2143+
onSubmit: ({ value }) =>
2144+
value.length > 0 ? undefined : 'first name is required',
2145+
},
2146+
})
2147+
2148+
field.mount()
2149+
2150+
await form.handleSubmit()
2151+
expect(field.getMeta().errors).toStrictEqual(['first name is required'])
2152+
2153+
// Entering a valid value clears the stale submit error
2154+
field.handleChange('John')
2155+
expect(field.getMeta().errors).toStrictEqual([])
2156+
expect(field.getMeta().isValid).toBe(true)
2157+
})
2158+
21012159
it('should show onMount errors', async () => {
21022160
const form = new FormApi({
21032161
defaultValues: {

0 commit comments

Comments
 (0)