Skip to content

Commit 5f1bc0b

Browse files
authored
fix(form-core): skip form validation for each single field on form submit (#1147)
1 parent 136a584 commit 5f1bc0b

File tree

5 files changed

+75
-28
lines changed

5 files changed

+75
-28
lines changed

docs/reference/classes/fieldapi.md

+10-4
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ Use `field.state.value` instead.
185185
handleBlur(): void
186186
```
187187

188-
Defined in: [packages/form-core/src/FieldApi.ts:1025](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1025)
188+
Defined in: [packages/form-core/src/FieldApi.ts:1030](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1030)
189189

190190
Handles the blur event.
191191

@@ -201,7 +201,7 @@ Handles the blur event.
201201
handleChange(updater): void
202202
```
203203

204-
Defined in: [packages/form-core/src/FieldApi.ts:1018](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1018)
204+
Defined in: [packages/form-core/src/FieldApi.ts:1023](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1023)
205205

206206
Handles the change event.
207207

@@ -394,7 +394,7 @@ Replaces a value at the specified index.
394394
setErrorMap(errorMap): void
395395
```
396396
397-
Defined in: [packages/form-core/src/FieldApi.ts:1045](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1045)
397+
Defined in: [packages/form-core/src/FieldApi.ts:1050](https://github.com/TanStack/form/blob/main/packages/form-core/src/FieldApi.ts#L1050)
398398
399399
Updates the field's errorMap
400400
@@ -516,7 +516,7 @@ Updates the field instance with new options.
516516
### validate()
517517
518518
```ts
519-
validate(cause):
519+
validate(cause, opts?):
520520
| ValidationError[]
521521
| Promise<ValidationError[]>
522522
```
@@ -531,6 +531,12 @@ Validates the field value.
531531
532532
`ValidationCause`
533533
534+
##### opts?
535+
536+
###### skipFormValidation
537+
538+
`boolean`
539+
534540
#### Returns
535541
536542
\| [`ValidationError`](../type-aliases/validationerror.md)[]

docs/reference/classes/formapi.md

+18-18
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ Defined in: [packages/form-core/src/FormApi.ts:378](https://github.com/TanStack/
119119
deleteField<TField>(field): void
120120
```
121121
122-
Defined in: [packages/form-core/src/FormApi.ts:1227](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1227)
122+
Defined in: [packages/form-core/src/FormApi.ts:1239](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1239)
123123
124124
#### Type Parameters
125125
@@ -143,7 +143,7 @@ Defined in: [packages/form-core/src/FormApi.ts:1227](https://github.com/TanStack
143143
getFieldInfo<TField>(field): FieldInfo<TFormData, TFormValidator>
144144
```
145145
146-
Defined in: [packages/form-core/src/FormApi.ts:1136](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1136)
146+
Defined in: [packages/form-core/src/FormApi.ts:1148](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1148)
147147
148148
Gets the field info of the specified field.
149149
@@ -169,7 +169,7 @@ Gets the field info of the specified field.
169169
getFieldMeta<TField>(field): undefined | FieldMeta
170170
```
171171
172-
Defined in: [packages/form-core/src/FormApi.ts:1127](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1127)
172+
Defined in: [packages/form-core/src/FormApi.ts:1139](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1139)
173173
174174
Gets the metadata of the specified field.
175175
@@ -195,7 +195,7 @@ Gets the metadata of the specified field.
195195
getFieldValue<TField>(field): DeepValue<TFormData, TField, IsNullable<TFormData>>
196196
```
197197
198-
Defined in: [packages/form-core/src/FormApi.ts:1120](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1120)
198+
Defined in: [packages/form-core/src/FormApi.ts:1132](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1132)
199199
200200
Gets the value of the specified field.
201201
@@ -221,7 +221,7 @@ Gets the value of the specified field.
221221
handleSubmit(): Promise<void>
222222
```
223223
224-
Defined in: [packages/form-core/src/FormApi.ts:1061](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1061)
224+
Defined in: [packages/form-core/src/FormApi.ts:1063](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1063)
225225
226226
Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
227227
@@ -241,7 +241,7 @@ insertFieldValue<TField>(
241241
opts?): Promise<void>
242242
```
243243
244-
Defined in: [packages/form-core/src/FormApi.ts:1259](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1259)
244+
Defined in: [packages/form-core/src/FormApi.ts:1271](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1271)
245245
246246
Inserts a value into an array field at the specified index, shifting the subsequent values to the right.
247247
@@ -301,7 +301,7 @@ moveFieldValues<TField>(
301301
opts?): void
302302
```
303303
304-
Defined in: [packages/form-core/src/FormApi.ts:1377](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1377)
304+
Defined in: [packages/form-core/src/FormApi.ts:1389](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1389)
305305
306306
Moves the value at the first specified index to the second specified index within an array field.
307307
@@ -342,7 +342,7 @@ pushFieldValue<TField>(
342342
opts?): void
343343
```
344344
345-
Defined in: [packages/form-core/src/FormApi.ts:1241](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1241)
345+
Defined in: [packages/form-core/src/FormApi.ts:1253](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1253)
346346
347347
Pushes a value into an array field.
348348
@@ -379,7 +379,7 @@ removeFieldValue<TField>(
379379
opts?): Promise<void>
380380
```
381381
382-
Defined in: [packages/form-core/src/FormApi.ts:1312](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1312)
382+
Defined in: [packages/form-core/src/FormApi.ts:1324](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1324)
383383
384384
Removes a value from an array field at the specified index.
385385
@@ -417,7 +417,7 @@ replaceFieldValue<TField>(
417417
opts?): Promise<void>
418418
```
419419
420-
Defined in: [packages/form-core/src/FormApi.ts:1286](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1286)
420+
Defined in: [packages/form-core/src/FormApi.ts:1298](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1298)
421421
422422
Replaces a value into an array field at the specified index.
423423
@@ -488,7 +488,7 @@ Optional options to control the reset behavior.
488488
resetFieldMeta<TField>(fieldMeta): Record<TField, FieldMeta>
489489
```
490490
491-
Defined in: [packages/form-core/src/FormApi.ts:1173](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1173)
491+
Defined in: [packages/form-core/src/FormApi.ts:1185](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1185)
492492
493493
#### Type Parameters
494494
@@ -512,7 +512,7 @@ Defined in: [packages/form-core/src/FormApi.ts:1173](https://github.com/TanStack
512512
setErrorMap(errorMap): void
513513
```
514514
515-
Defined in: [packages/form-core/src/FormApi.ts:1401](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1401)
515+
Defined in: [packages/form-core/src/FormApi.ts:1413](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1413)
516516
517517
Updates the form's errorMap
518518
@@ -534,7 +534,7 @@ Updates the form's errorMap
534534
setFieldMeta<TField>(field, updater): void
535535
```
536536
537-
Defined in: [packages/form-core/src/FormApi.ts:1155](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1155)
537+
Defined in: [packages/form-core/src/FormApi.ts:1167](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1167)
538538
539539
Updates the metadata of the specified field.
540540
@@ -567,7 +567,7 @@ setFieldValue<TField>(
567567
opts?): void
568568
```
569569
570-
Defined in: [packages/form-core/src/FormApi.ts:1197](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1197)
570+
Defined in: [packages/form-core/src/FormApi.ts:1209](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1209)
571571
572572
Sets the value of the specified field and optionally updates the touched state.
573573
@@ -605,7 +605,7 @@ swapFieldValues<TField>(
605605
opts?): void
606606
```
607607
608-
Defined in: [packages/form-core/src/FormApi.ts:1351](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1351)
608+
Defined in: [packages/form-core/src/FormApi.ts:1363](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1363)
609609
610610
Swaps the values at the specified indices within an array field.
611611
@@ -667,7 +667,7 @@ validateAllFields(cause): Promise<ValidationError[]>
667667
668668
Defined in: [packages/form-core/src/FormApi.ts:727](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L727)
669669
670-
Validates form and all fields in using the correct handlers for a given validation cause.
670+
Validates all fields using the correct handlers for a given validation cause.
671671
672672
#### Parameters
673673
@@ -690,7 +690,7 @@ validateArrayFieldsStartingFrom<TField>(
690690
cause): Promise<ValidationError[]>
691691
```
692692
693-
Defined in: [packages/form-core/src/FormApi.ts:755](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L755)
693+
Defined in: [packages/form-core/src/FormApi.ts:757](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L757)
694694
695695
Validates the children of a specified array in the form starting from a given index until the end using the correct handlers for a given validation type.
696696
@@ -726,7 +726,7 @@ validateField<TField>(field, cause):
726726
| Promise<ValidationError[]>
727727
```
728728
729-
Defined in: [packages/form-core/src/FormApi.ts:794](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L794)
729+
Defined in: [packages/form-core/src/FormApi.ts:796](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L796)
730730
731731
Validates a specified field in the form using the correct handlers for a given validation type.
732732

packages/form-core/src/FieldApi.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -989,12 +989,15 @@ export class FieldApi<
989989
*/
990990
validate = (
991991
cause: ValidationCause,
992+
opts?: { skipFormValidation?: boolean },
992993
): ValidationError[] | Promise<ValidationError[]> => {
993994
// If the field is pristine, do not validate
994995
if (!this.state.meta.isTouched) return []
995996

996997
// Attempt to sync validate first
997-
const { fieldsErrorMap } = this.form.validateSync(cause)
998+
const { fieldsErrorMap } = opts?.skipFormValidation
999+
? { fieldsErrorMap: {} as never }
1000+
: this.form.validateSync(cause)
9981001
const { hasErrored } = this.validateSync(
9991002
cause,
10001003
fieldsErrorMap[this.name] ?? {},
@@ -1008,7 +1011,9 @@ export class FieldApi<
10081011
}
10091012

10101013
// No error? Attempt async validation
1011-
const formValidationResultPromise = this.form.validateAsync(cause)
1014+
const formValidationResultPromise = opts?.skipFormValidation
1015+
? Promise.resolve({})
1016+
: this.form.validateAsync(cause)
10121017
return this.validateAsync(cause, formValidationResultPromise)
10131018
}
10141019

packages/form-core/src/FormApi.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ export class FormApi<
722722
}
723723

724724
/**
725-
* Validates form and all fields in using the correct handlers for a given validation cause.
725+
* Validates all fields using the correct handlers for a given validation cause.
726726
*/
727727
validateAllFields = async (cause: ValidationCause) => {
728728
const fieldValidationPromises: Promise<ValidationError[]>[] = [] as any
@@ -735,7 +735,9 @@ export class FormApi<
735735
// Validate the field
736736
fieldValidationPromises.push(
737737
// Remember, `validate` is either a sync operation or a promise
738-
Promise.resolve().then(() => fieldInstance.validate(cause)),
738+
Promise.resolve().then(() =>
739+
fieldInstance.validate(cause, { skipFormValidation: true }),
740+
),
739741
)
740742
// If any fields are not touched
741743
if (!field.instance.state.meta.isTouched) {
@@ -1076,9 +1078,19 @@ export class FormApi<
10761078
this.baseStore.setState((prev) => ({ ...prev, isSubmitting: false }))
10771079
}
10781080

1079-
// Validate form and all fields
10801081
await this.validateAllFields('submit')
10811082

1083+
if (!this.state.isFieldsValid) {
1084+
done()
1085+
this.options.onSubmitInvalid?.({
1086+
value: this.state.values,
1087+
formApi: this,
1088+
})
1089+
return
1090+
}
1091+
1092+
await this.validate('submit')
1093+
10821094
// Fields are invalid, do not submit
10831095
if (!this.state.isValid) {
10841096
done()

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

+25-1
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,31 @@ describe('form api', () => {
12631263
])
12641264
})
12651265

1266-
it('should run form validation once during submit', async () => {
1266+
it('should run form submit validation once during submit, not once per field', async () => {
1267+
const formSubmit = vi.fn().mockReturnValue(false)
1268+
1269+
const form = new FormApi({
1270+
defaultValues: {
1271+
firstName: '',
1272+
lastName: '',
1273+
age: 0,
1274+
},
1275+
validators: {
1276+
onSubmit: formSubmit,
1277+
},
1278+
})
1279+
form.mount()
1280+
1281+
new FieldApi({ form, name: 'firstName' }).mount()
1282+
new FieldApi({ form, name: 'lastName' }).mount()
1283+
new FieldApi({ form, name: 'age' }).mount()
1284+
1285+
await form.handleSubmit()
1286+
1287+
expect(formSubmit).toHaveBeenCalledOnce()
1288+
})
1289+
1290+
it('should run form async submit validation once during submit', async () => {
12671291
vi.useFakeTimers()
12681292
const formSubmit = vi.fn()
12691293
const fieldChangeValidator = vi

0 commit comments

Comments
 (0)