1
1
import { Derived , Store , batch } from '@tanstack/store'
2
2
import {
3
3
deleteBy ,
4
+ determineFormLevelErrorSourceAndValue ,
4
5
functionalUpdate ,
5
6
getAsyncValidatorArray ,
6
7
getBy ,
@@ -733,22 +734,6 @@ export class FormApi<
733
734
*/
734
735
prevTransformArray : unknown [ ] = [ ]
735
736
736
- /**
737
- * @private Persistent store of all field validation errors originating from form-level validators.
738
- * Maintains the cumulative state across validation cycles, including cleared errors (undefined values).
739
- * This map preserves the complete validation state for all fields.
740
- */
741
- cumulativeFieldsErrorMap : FormErrorMapFromValidator <
742
- TFormData ,
743
- TOnMount ,
744
- TOnChange ,
745
- TOnChangeAsync ,
746
- TOnBlur ,
747
- TOnBlurAsync ,
748
- TOnSubmit ,
749
- TOnSubmitAsync
750
- > = { }
751
-
752
737
/**
753
738
* Constructs a new `FormApi` instance with the given form options.
754
739
*/
@@ -1306,56 +1291,56 @@ export class FormApi<
1306
1291
1307
1292
const errorMapKey = getErrorMapKey ( validateObj . cause )
1308
1293
1309
- if ( fieldErrors ) {
1310
- for ( const [ field , fieldError ] of Object . entries ( fieldErrors ) as [
1311
- DeepKeys < TFormData > ,
1312
- ValidationError ,
1313
- ] [ ] ) {
1314
- const oldErrorMap = this . cumulativeFieldsErrorMap [ field ] || { }
1315
- const newErrorMap = {
1316
- ...oldErrorMap ,
1317
- [ errorMapKey ] : fieldError ,
1318
- }
1319
- currentValidationErrorMap [ field ] = newErrorMap
1320
- this . cumulativeFieldsErrorMap [ field ] = newErrorMap
1294
+ for ( const field of Object . keys (
1295
+ this . state . fieldMeta ,
1296
+ ) as DeepKeys < TFormData > [ ] ) {
1297
+ const fieldMeta = this . getFieldMeta ( field )
1298
+ if ( ! fieldMeta ) continue
1299
+
1300
+ const {
1301
+ errorMap : currentErrorMap ,
1302
+ errorSourceMap : currentErrorMapSource ,
1303
+ } = fieldMeta
1304
+
1305
+ const newFormValidatorError = fieldErrors ?. [ field ]
1306
+
1307
+ const { newErrorValue, newSource } =
1308
+ determineFormLevelErrorSourceAndValue ( {
1309
+ newFormValidatorError,
1310
+ isPreviousErrorFromFormValidator :
1311
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1312
+ currentErrorMapSource ?. [ errorMapKey ] === 'form' ,
1313
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1314
+ previousErrorValue : currentErrorMap ?. [ errorMapKey ] ,
1315
+ } )
1321
1316
1322
- const fieldMeta = this . getFieldMeta ( field )
1323
- if ( fieldMeta && fieldMeta . errorMap [ errorMapKey ] !== fieldError ) {
1324
- this . setFieldMeta ( field , ( prev ) => ( {
1325
- ...prev ,
1326
- errorMap : {
1327
- ...prev . errorMap ,
1328
- [ errorMapKey ] : fieldError ,
1329
- } ,
1330
- } ) )
1317
+ if ( newSource === 'form' ) {
1318
+ currentValidationErrorMap [ field ] = {
1319
+ ...currentValidationErrorMap [ field ] ,
1320
+ [ errorMapKey ] : newFormValidatorError ,
1331
1321
}
1332
1322
}
1333
- }
1334
1323
1335
- for ( const field of Object . keys ( this . cumulativeFieldsErrorMap ) as Array <
1336
- DeepKeys < TFormData >
1337
- > ) {
1338
- const fieldMeta = this . getFieldMeta ( field )
1339
1324
if (
1340
- fieldMeta ?. errorMap [ errorMapKey ] &&
1341
- ! currentValidationErrorMap [ field ] ?. [ errorMapKey ]
1325
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1326
+ currentErrorMap ?. [ errorMapKey ] !== newErrorValue
1342
1327
) {
1343
- this . cumulativeFieldsErrorMap [ field ] = {
1344
- ...this . cumulativeFieldsErrorMap [ field ] ,
1345
- [ errorMapKey ] : undefined ,
1346
- }
1347
-
1348
1328
this . setFieldMeta ( field , ( prev ) => ( {
1349
1329
...prev ,
1350
1330
errorMap : {
1351
1331
...prev . errorMap ,
1352
- [ errorMapKey ] : undefined ,
1332
+ [ errorMapKey ] : newErrorValue ,
1333
+ } ,
1334
+ errorSourceMap : {
1335
+ ...prev . errorSourceMap ,
1336
+ [ errorMapKey ] : newSource ,
1353
1337
} ,
1354
1338
} ) )
1355
1339
}
1356
1340
}
1357
1341
1358
- if ( this . state . errorMap [ errorMapKey ] !== formError ) {
1342
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1343
+ if ( this . state . errorMap ?. [ errorMapKey ] !== formError ) {
1359
1344
this . baseStore . setState ( ( prev ) => ( {
1360
1345
...prev ,
1361
1346
errorMap : {
@@ -1376,7 +1361,8 @@ export class FormApi<
1376
1361
*/
1377
1362
const submitErrKey = getErrorMapKey ( 'submit' )
1378
1363
if (
1379
- this . state . errorMap [ submitErrKey ] &&
1364
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1365
+ this . state . errorMap ?. [ submitErrKey ] &&
1380
1366
cause !== 'submit' &&
1381
1367
! hasErrored
1382
1368
) {
@@ -1422,7 +1408,7 @@ export class FormApi<
1422
1408
*/
1423
1409
const promises : Promise < ValidationPromiseResult < TFormData > > [ ] = [ ]
1424
1410
1425
- let fieldErrors :
1411
+ let fieldErrorsFromFormValidators :
1426
1412
| Partial < Record < DeepKeys < TFormData > , ValidationError > >
1427
1413
| undefined
1428
1414
@@ -1473,26 +1459,56 @@ export class FormApi<
1473
1459
normalizeError < TFormData > ( rawError )
1474
1460
1475
1461
if ( fieldErrorsFromNormalizeError ) {
1476
- fieldErrors = fieldErrors
1477
- ? { ...fieldErrors , ...fieldErrorsFromNormalizeError }
1462
+ fieldErrorsFromFormValidators = fieldErrorsFromFormValidators
1463
+ ? {
1464
+ ...fieldErrorsFromFormValidators ,
1465
+ ...fieldErrorsFromNormalizeError ,
1466
+ }
1478
1467
: fieldErrorsFromNormalizeError
1479
1468
}
1480
1469
const errorMapKey = getErrorMapKey ( validateObj . cause )
1481
1470
1482
- if ( fieldErrors ) {
1483
- for ( const [ field , fieldError ] of Object . entries ( fieldErrors ) ) {
1484
- const fieldMeta = this . getFieldMeta ( field as DeepKeys < TFormData > )
1485
- if ( fieldMeta && fieldMeta . errorMap [ errorMapKey ] !== fieldError ) {
1486
- this . setFieldMeta ( field as DeepKeys < TFormData > , ( prev ) => ( {
1487
- ...prev ,
1488
- errorMap : {
1489
- ...prev . errorMap ,
1490
- [ errorMapKey ] : fieldError ,
1491
- } ,
1492
- } ) )
1493
- }
1471
+ for ( const field of Object . keys (
1472
+ this . state . fieldMeta ,
1473
+ ) as DeepKeys < TFormData > [ ] ) {
1474
+ const fieldMeta = this . getFieldMeta ( field )
1475
+ if ( ! fieldMeta ) continue
1476
+
1477
+ const {
1478
+ errorMap : currentErrorMap ,
1479
+ errorSourceMap : currentErrorMapSource ,
1480
+ } = fieldMeta
1481
+
1482
+ const newFormValidatorError = fieldErrorsFromFormValidators ?. [ field ]
1483
+
1484
+ const { newErrorValue, newSource } =
1485
+ determineFormLevelErrorSourceAndValue ( {
1486
+ newFormValidatorError,
1487
+ isPreviousErrorFromFormValidator :
1488
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1489
+ currentErrorMapSource ?. [ errorMapKey ] === 'form' ,
1490
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1491
+ previousErrorValue : currentErrorMap ?. [ errorMapKey ] ,
1492
+ } )
1493
+
1494
+ if (
1495
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1496
+ currentErrorMap ?. [ errorMapKey ] !== newErrorValue
1497
+ ) {
1498
+ this . setFieldMeta ( field , ( prev ) => ( {
1499
+ ...prev ,
1500
+ errorMap : {
1501
+ ...prev . errorMap ,
1502
+ [ errorMapKey ] : newErrorValue ,
1503
+ } ,
1504
+ errorSourceMap : {
1505
+ ...prev . errorSourceMap ,
1506
+ [ errorMapKey ] : newSource ,
1507
+ } ,
1508
+ } ) )
1494
1509
}
1495
1510
}
1511
+
1496
1512
this . baseStore . setState ( ( prev ) => ( {
1497
1513
...prev ,
1498
1514
errorMap : {
@@ -1501,7 +1517,11 @@ export class FormApi<
1501
1517
} ,
1502
1518
} ) )
1503
1519
1504
- resolve ( fieldErrors ? { fieldErrors, errorMapKey } : undefined )
1520
+ resolve (
1521
+ fieldErrorsFromFormValidators
1522
+ ? { fieldErrors : fieldErrorsFromFormValidators , errorMapKey }
1523
+ : undefined ,
1524
+ )
1505
1525
} ) ,
1506
1526
)
1507
1527
}
0 commit comments