11import { Derived , Store , batch } from '@tanstack/store'
22import {
33 deleteBy ,
4+ determineFormLevelErrorSourceAndValue ,
45 functionalUpdate ,
56 getAsyncValidatorArray ,
67 getBy ,
@@ -733,22 +734,6 @@ export class FormApi<
733734 */
734735 prevTransformArray : unknown [ ] = [ ]
735736
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-
752737 /**
753738 * Constructs a new `FormApi` instance with the given form options.
754739 */
@@ -1306,56 +1291,56 @@ export class FormApi<
13061291
13071292 const errorMapKey = getErrorMapKey ( validateObj . cause )
13081293
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+ } )
13211316
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 ,
13311321 }
13321322 }
1333- }
13341323
1335- for ( const field of Object . keys ( this . cumulativeFieldsErrorMap ) as Array <
1336- DeepKeys < TFormData >
1337- > ) {
1338- const fieldMeta = this . getFieldMeta ( field )
13391324 if (
1340- fieldMeta ?. errorMap [ errorMapKey ] &&
1341- ! currentValidationErrorMap [ field ] ?. [ errorMapKey ]
1325+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1326+ currentErrorMap ?. [ errorMapKey ] !== newErrorValue
13421327 ) {
1343- this . cumulativeFieldsErrorMap [ field ] = {
1344- ...this . cumulativeFieldsErrorMap [ field ] ,
1345- [ errorMapKey ] : undefined ,
1346- }
1347-
13481328 this . setFieldMeta ( field , ( prev ) => ( {
13491329 ...prev ,
13501330 errorMap : {
13511331 ...prev . errorMap ,
1352- [ errorMapKey ] : undefined ,
1332+ [ errorMapKey ] : newErrorValue ,
1333+ } ,
1334+ errorSourceMap : {
1335+ ...prev . errorSourceMap ,
1336+ [ errorMapKey ] : newSource ,
13531337 } ,
13541338 } ) )
13551339 }
13561340 }
13571341
1358- if ( this . state . errorMap [ errorMapKey ] !== formError ) {
1342+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1343+ if ( this . state . errorMap ?. [ errorMapKey ] !== formError ) {
13591344 this . baseStore . setState ( ( prev ) => ( {
13601345 ...prev ,
13611346 errorMap : {
@@ -1376,7 +1361,8 @@ export class FormApi<
13761361 */
13771362 const submitErrKey = getErrorMapKey ( 'submit' )
13781363 if (
1379- this . state . errorMap [ submitErrKey ] &&
1364+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1365+ this . state . errorMap ?. [ submitErrKey ] &&
13801366 cause !== 'submit' &&
13811367 ! hasErrored
13821368 ) {
@@ -1422,7 +1408,7 @@ export class FormApi<
14221408 */
14231409 const promises : Promise < ValidationPromiseResult < TFormData > > [ ] = [ ]
14241410
1425- let fieldErrors :
1411+ let fieldErrorsFromFormValidators :
14261412 | Partial < Record < DeepKeys < TFormData > , ValidationError > >
14271413 | undefined
14281414
@@ -1473,26 +1459,56 @@ export class FormApi<
14731459 normalizeError < TFormData > ( rawError )
14741460
14751461 if ( fieldErrorsFromNormalizeError ) {
1476- fieldErrors = fieldErrors
1477- ? { ...fieldErrors , ...fieldErrorsFromNormalizeError }
1462+ fieldErrorsFromFormValidators = fieldErrorsFromFormValidators
1463+ ? {
1464+ ...fieldErrorsFromFormValidators ,
1465+ ...fieldErrorsFromNormalizeError ,
1466+ }
14781467 : fieldErrorsFromNormalizeError
14791468 }
14801469 const errorMapKey = getErrorMapKey ( validateObj . cause )
14811470
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+ } ) )
14941509 }
14951510 }
1511+
14961512 this . baseStore . setState ( ( prev ) => ( {
14971513 ...prev ,
14981514 errorMap : {
@@ -1501,7 +1517,11 @@ export class FormApi<
15011517 } ,
15021518 } ) )
15031519
1504- resolve ( fieldErrors ? { fieldErrors, errorMapKey } : undefined )
1520+ resolve (
1521+ fieldErrorsFromFormValidators
1522+ ? { fieldErrors : fieldErrorsFromFormValidators , errorMapKey }
1523+ : undefined ,
1524+ )
15051525 } ) ,
15061526 )
15071527 }
0 commit comments