Skip to content

Commit 30b90a6

Browse files
juanvilladevautofix-ci[bot]crutchcorn
authored
fix(form-core): #1182 remove stale form level validation errors. Main… (#1186)
* fix(form-core): #1182 remove stale form level validation errors. Maintain cummulative errorMap instead of snapshot of previous validation cycle. * ci: apply automated fixes and generate docs * ci: apply automated fixes and generate docs --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Corbin Crutchley <[email protected]>
1 parent 6c1017f commit 30b90a6

File tree

3 files changed

+109
-39
lines changed

3 files changed

+109
-39
lines changed

Diff for: docs/reference/classes/formapi.md

+24-24
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ However, if you need to create a new instance manually, you can do so by calling
4545
new FormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnServer, TSubmitMeta>(opts?): FormApi<TFormData, TOnMount, TOnChange, TOnChangeAsync, TOnBlur, TOnBlurAsync, TOnSubmit, TOnSubmitAsync, TOnServer, TSubmitMeta>
4646
```
4747
48-
Defined in: [packages/form-core/src/FormApi.ts:739](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L739)
48+
Defined in: [packages/form-core/src/FormApi.ts:741](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L741)
4949
5050
Constructs a new `FormApi` instance with the given form options.
5151
@@ -137,7 +137,7 @@ Defined in: [packages/form-core/src/FormApi.ts:713](https://github.com/TanStack/
137137
deleteField<TField>(field): void
138138
```
139139
140-
Defined in: [packages/form-core/src/FormApi.ts:1747](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1747)
140+
Defined in: [packages/form-core/src/FormApi.ts:1756](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1756)
141141
142142
#### Type Parameters
143143
@@ -161,7 +161,7 @@ Defined in: [packages/form-core/src/FormApi.ts:1747](https://github.com/TanStack
161161
getAllErrors(): object
162162
```
163163
164-
Defined in: [packages/form-core/src/FormApi.ts:1958](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1958)
164+
Defined in: [packages/form-core/src/FormApi.ts:1967](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1967)
165165
166166
Returns form and field level errors
167167
@@ -212,7 +212,7 @@ errors: (
212212
getFieldInfo<TField>(field): FieldInfo<TFormData>
213213
```
214214
215-
Defined in: [packages/form-core/src/FormApi.ts:1656](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1656)
215+
Defined in: [packages/form-core/src/FormApi.ts:1665](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1665)
216216
217217
Gets the field info of the specified field.
218218
@@ -238,7 +238,7 @@ Gets the field info of the specified field.
238238
getFieldMeta<TField>(field): undefined | AnyFieldMeta
239239
```
240240
241-
Defined in: [packages/form-core/src/FormApi.ts:1647](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1647)
241+
Defined in: [packages/form-core/src/FormApi.ts:1656](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1656)
242242
243243
Gets the metadata of the specified field.
244244
@@ -264,7 +264,7 @@ Gets the metadata of the specified field.
264264
getFieldValue<TField>(field): DeepValue<TFormData, TField>
265265
```
266266
267-
Defined in: [packages/form-core/src/FormApi.ts:1640](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1640)
267+
Defined in: [packages/form-core/src/FormApi.ts:1649](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1649)
268268
269269
Gets the value of the specified field.
270270
@@ -292,7 +292,7 @@ Gets the value of the specified field.
292292
handleSubmit(): Promise<void>
293293
```
294294
295-
Defined in: [packages/form-core/src/FormApi.ts:1565](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1565)
295+
Defined in: [packages/form-core/src/FormApi.ts:1574](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1574)
296296
297297
Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
298298
@@ -306,7 +306,7 @@ Handles the form submission, performs validation, and calls the appropriate onSu
306306
handleSubmit(submitMeta): Promise<void>
307307
```
308308
309-
Defined in: [packages/form-core/src/FormApi.ts:1566](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1566)
309+
Defined in: [packages/form-core/src/FormApi.ts:1575](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1575)
310310
311311
Handles the form submission, performs validation, and calls the appropriate onSubmit or onInvalidSubmit callbacks.
312312
@@ -332,7 +332,7 @@ insertFieldValue<TField>(
332332
opts?): Promise<void>
333333
```
334334
335-
Defined in: [packages/form-core/src/FormApi.ts:1776](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1776)
335+
Defined in: [packages/form-core/src/FormApi.ts:1785](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1785)
336336
337337
#### Type Parameters
338338
@@ -368,7 +368,7 @@ Defined in: [packages/form-core/src/FormApi.ts:1776](https://github.com/TanStack
368368
mount(): () => void
369369
```
370370
371-
Defined in: [packages/form-core/src/FormApi.ts:1040](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1040)
371+
Defined in: [packages/form-core/src/FormApi.ts:1042](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1042)
372372
373373
#### Returns
374374
@@ -390,7 +390,7 @@ moveFieldValues<TField>(
390390
opts?): void
391391
```
392392
393-
Defined in: [packages/form-core/src/FormApi.ts:1905](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1905)
393+
Defined in: [packages/form-core/src/FormApi.ts:1914](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1914)
394394
395395
Moves the value at the first specified index to the second specified index within an array field.
396396
@@ -431,7 +431,7 @@ pushFieldValue<TField>(
431431
opts?): void
432432
```
433433
434-
Defined in: [packages/form-core/src/FormApi.ts:1761](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1761)
434+
Defined in: [packages/form-core/src/FormApi.ts:1770](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1770)
435435
436436
Pushes a value into an array field.
437437
@@ -468,7 +468,7 @@ removeFieldValue<TField>(
468468
opts?): Promise<void>
469469
```
470470
471-
Defined in: [packages/form-core/src/FormApi.ts:1834](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1834)
471+
Defined in: [packages/form-core/src/FormApi.ts:1843](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1843)
472472
473473
Removes a value from an array field at the specified index.
474474
@@ -506,7 +506,7 @@ replaceFieldValue<TField>(
506506
opts?): Promise<void>
507507
```
508508
509-
Defined in: [packages/form-core/src/FormApi.ts:1808](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1808)
509+
Defined in: [packages/form-core/src/FormApi.ts:1817](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1817)
510510
511511
Replaces a value into an array field at the specified index.
512512
@@ -544,7 +544,7 @@ Replaces a value into an array field at the specified index.
544544
reset(values?, opts?): void
545545
```
546546
547-
Defined in: [packages/form-core/src/FormApi.ts:1125](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1125)
547+
Defined in: [packages/form-core/src/FormApi.ts:1127](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1127)
548548
549549
Resets the form state to the default values.
550550
If values are provided, the form will be reset to those values instead and the default values will be updated.
@@ -577,7 +577,7 @@ Optional options to control the reset behavior.
577577
resetFieldMeta<TField>(fieldMeta): Record<TField, AnyFieldMeta>
578578
```
579579
580-
Defined in: [packages/form-core/src/FormApi.ts:1693](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1693)
580+
Defined in: [packages/form-core/src/FormApi.ts:1702](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1702)
581581
582582
#### Type Parameters
583583
@@ -601,7 +601,7 @@ Defined in: [packages/form-core/src/FormApi.ts:1693](https://github.com/TanStack
601601
setErrorMap(errorMap): void
602602
```
603603
604-
Defined in: [packages/form-core/src/FormApi.ts:1932](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1932)
604+
Defined in: [packages/form-core/src/FormApi.ts:1941](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1941)
605605
606606
Updates the form's errorMap
607607
@@ -623,7 +623,7 @@ Updates the form's errorMap
623623
setFieldMeta<TField>(field, updater): void
624624
```
625625
626-
Defined in: [packages/form-core/src/FormApi.ts:1675](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1675)
626+
Defined in: [packages/form-core/src/FormApi.ts:1684](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1684)
627627
628628
Updates the metadata of the specified field.
629629
@@ -656,7 +656,7 @@ setFieldValue<TField>(
656656
opts?): void
657657
```
658658
659-
Defined in: [packages/form-core/src/FormApi.ts:1717](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1717)
659+
Defined in: [packages/form-core/src/FormApi.ts:1726](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1726)
660660
661661
Sets the value of the specified field and optionally updates the touched state.
662662
@@ -694,7 +694,7 @@ swapFieldValues<TField>(
694694
opts?): void
695695
```
696696
697-
Defined in: [packages/form-core/src/FormApi.ts:1876](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1876)
697+
Defined in: [packages/form-core/src/FormApi.ts:1885](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1885)
698698
699699
Swaps the values at the specified indices within an array field.
700700
@@ -732,7 +732,7 @@ Swaps the values at the specified indices within an array field.
732732
update(options?): void
733733
```
734734
735-
Defined in: [packages/form-core/src/FormApi.ts:1057](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1057)
735+
Defined in: [packages/form-core/src/FormApi.ts:1059](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1059)
736736
737737
Updates the form options and form state.
738738
@@ -754,7 +754,7 @@ Updates the form options and form state.
754754
validateAllFields(cause): Promise<unknown[]>
755755
```
756756
757-
Defined in: [packages/form-core/src/FormApi.ts:1151](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1151)
757+
Defined in: [packages/form-core/src/FormApi.ts:1153](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1153)
758758
759759
Validates all fields using the correct handlers for a given validation cause.
760760
@@ -779,7 +779,7 @@ validateArrayFieldsStartingFrom<TField>(
779779
cause): Promise<unknown[]>
780780
```
781781
782-
Defined in: [packages/form-core/src/FormApi.ts:1181](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1181)
782+
Defined in: [packages/form-core/src/FormApi.ts:1183](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1183)
783783
784784
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.
785785
@@ -813,7 +813,7 @@ Validates the children of a specified array in the form starting from a given in
813813
validateField<TField>(field, cause): unknown[] | Promise<unknown[]>
814814
```
815815
816-
Defined in: [packages/form-core/src/FormApi.ts:1220](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1220)
816+
Defined in: [packages/form-core/src/FormApi.ts:1222](https://github.com/TanStack/form/blob/main/packages/form-core/src/FormApi.ts#L1222)
817817
818818
Validates a specified field in the form using the correct handlers for a given validation type.
819819

Diff for: packages/form-core/src/FormApi.ts

+23-14
Original file line numberDiff line numberDiff line change
@@ -720,9 +720,11 @@ export class FormApi<
720720
prevTransformArray: unknown[] = []
721721

722722
/**
723-
* @private map of errors originated from form level validators
723+
* @private Persistent store of all field validation errors originating from form-level validators.
724+
* Maintains the cumulative state across validation cycles, including cleared errors (undefined values).
725+
* This map preserves the complete validation state for all fields.
724726
*/
725-
prevFieldsErrorMap: FormErrorMapFromValidator<
727+
cumulativeFieldsErrorMap: FormErrorMapFromValidator<
726728
TFormData,
727729
TOnMount,
728730
TOnChange,
@@ -1256,7 +1258,8 @@ export class FormApi<
12561258
const validates = getSyncValidatorArray(cause, this.options)
12571259
let hasErrored = false as boolean
12581260

1259-
const newFieldsErrorMap: FormErrorMapFromValidator<
1261+
// This map will only include fields that have errors in the current validation cycle
1262+
const currentValidationErrorMap: FormErrorMapFromValidator<
12601263
TFormData,
12611264
TOnMount,
12621265
TOnChange,
@@ -1286,18 +1289,21 @@ export class FormApi<
12861289
const errorMapKey = getErrorMapKey(validateObj.cause)
12871290

12881291
if (fieldErrors) {
1289-
for (const [field, fieldError] of Object.entries(fieldErrors)) {
1290-
const oldErrorMap =
1291-
newFieldsErrorMap[field as DeepKeys<TFormData>] || {}
1292+
for (const [field, fieldError] of Object.entries(fieldErrors) as [
1293+
DeepKeys<TFormData>,
1294+
ValidationError,
1295+
][]) {
1296+
const oldErrorMap = this.cumulativeFieldsErrorMap[field] || {}
12921297
const newErrorMap = {
12931298
...oldErrorMap,
12941299
[errorMapKey]: fieldError,
12951300
}
1296-
newFieldsErrorMap[field as DeepKeys<TFormData>] = newErrorMap
1301+
currentValidationErrorMap[field] = newErrorMap
1302+
this.cumulativeFieldsErrorMap[field] = newErrorMap
12971303

1298-
const fieldMeta = this.getFieldMeta(field as DeepKeys<TFormData>)
1304+
const fieldMeta = this.getFieldMeta(field)
12991305
if (fieldMeta && fieldMeta.errorMap[errorMapKey] !== fieldError) {
1300-
this.setFieldMeta(field as DeepKeys<TFormData>, (prev) => ({
1306+
this.setFieldMeta(field, (prev) => ({
13011307
...prev,
13021308
errorMap: {
13031309
...prev.errorMap,
@@ -1308,14 +1314,19 @@ export class FormApi<
13081314
}
13091315
}
13101316

1311-
for (const field of Object.keys(this.prevFieldsErrorMap) as Array<
1317+
for (const field of Object.keys(this.cumulativeFieldsErrorMap) as Array<
13121318
DeepKeys<TFormData>
13131319
>) {
13141320
const fieldMeta = this.getFieldMeta(field)
13151321
if (
13161322
fieldMeta?.errorMap[errorMapKey] &&
1317-
!newFieldsErrorMap[field]?.[errorMapKey]
1323+
!currentValidationErrorMap[field]?.[errorMapKey]
13181324
) {
1325+
this.cumulativeFieldsErrorMap[field] = {
1326+
...this.cumulativeFieldsErrorMap[field],
1327+
[errorMapKey]: undefined,
1328+
}
1329+
13191330
this.setFieldMeta(field, (prev) => ({
13201331
...prev,
13211332
errorMap: {
@@ -1361,9 +1372,7 @@ export class FormApi<
13611372
}
13621373
})
13631374

1364-
this.prevFieldsErrorMap = newFieldsErrorMap
1365-
1366-
return { hasErrored, fieldsErrorMap: newFieldsErrorMap }
1375+
return { hasErrored, fieldsErrorMap: currentValidationErrorMap }
13671376
}
13681377

13691378
/**

Diff for: packages/form-core/tests/FormApi.spec.ts

+62-1
Original file line numberDiff line numberDiff line change
@@ -2643,7 +2643,68 @@ describe('form api', () => {
26432643
)
26442644
})
26452645

2646-
it('clears previous form level errors when they are no longer valid', () => {
2646+
it('clears errors on all fields affected by form validation when condition resolves', () => {
2647+
const form = new FormApi({
2648+
defaultValues: {
2649+
firstName: '',
2650+
lastName: '',
2651+
},
2652+
validators: {
2653+
onChange: ({ value }) => {
2654+
if (value.firstName && value.lastName) {
2655+
return {
2656+
fields: {
2657+
firstName: 'Do not enter both firstName and lastName',
2658+
lastName: 'Do not enter both firstName and lastName',
2659+
},
2660+
}
2661+
}
2662+
return null
2663+
},
2664+
},
2665+
})
2666+
form.mount()
2667+
2668+
const firstNameField = new FieldApi({
2669+
form,
2670+
name: 'firstName',
2671+
})
2672+
firstNameField.mount()
2673+
2674+
const lastNameField = new FieldApi({
2675+
form,
2676+
name: 'lastName',
2677+
})
2678+
lastNameField.mount()
2679+
2680+
// Set values to trigger validation errors
2681+
firstNameField.setValue('John')
2682+
lastNameField.setValue('Doe')
2683+
2684+
// Verify both fields have errors
2685+
expect(firstNameField.state.meta.errors).toContain(
2686+
'Do not enter both firstName and lastName',
2687+
)
2688+
expect(lastNameField.state.meta.errors).toContain(
2689+
'Do not enter both firstName and lastName',
2690+
)
2691+
2692+
// Clear one field's value
2693+
firstNameField.setValue('')
2694+
2695+
// Verify both fields have their errors cleared
2696+
expect(firstNameField.state.meta.errors).toStrictEqual([])
2697+
expect(lastNameField.state.meta.errors).toStrictEqual([])
2698+
2699+
// Verify previous error map still contains values for the fields as it should indicate the last error map processed for the fields
2700+
const cumulativeFieldsErrorMap = form.cumulativeFieldsErrorMap
2701+
expect(cumulativeFieldsErrorMap.firstName).toBeDefined()
2702+
expect(cumulativeFieldsErrorMap.lastName).toBeDefined()
2703+
expect(cumulativeFieldsErrorMap.firstName?.onChange).toBeUndefined()
2704+
expect(cumulativeFieldsErrorMap.lastName?.onChange).toBeUndefined()
2705+
})
2706+
2707+
it('clears previous form level errors for subfields when they are no longer valid', () => {
26472708
const form = new FormApi({
26482709
defaultValues: {
26492710
interests: [

0 commit comments

Comments
 (0)