From b53bf0d9bf2e89fde9a491bf9cce88717e0c998b Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Thu, 14 Aug 2025 14:54:18 +0900 Subject: [PATCH 1/6] test: field mount bug test --- .../tests/hidden-field-mount.test.ts | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 packages/form-core/tests/hidden-field-mount.test.ts diff --git a/packages/form-core/tests/hidden-field-mount.test.ts b/packages/form-core/tests/hidden-field-mount.test.ts new file mode 100644 index 000000000..a2d0c4713 --- /dev/null +++ b/packages/form-core/tests/hidden-field-mount.test.ts @@ -0,0 +1,115 @@ +import { describe, expect, it } from 'vitest' +import { FieldApi, FormApi } from '../src' + +describe('Hidden field mount validation', () => { + it('should display errors on delayed field mount', async () => { + const form = new FormApi({ + defaultValues: { + hiddenField: '', + visibleField: '' + }, + validators: { + onMount: ({ value }) => { + const errors: Record = {} + if (!value.hiddenField) { + errors.hiddenField = 'Hidden field is required' + } + if (!value.visibleField) { + errors.visibleField = 'Visible field is required' + } + return { fields: errors } + } + } + }) + + form.mount() + + const visibleField = new FieldApi({ + form, + name: 'visibleField' + }) + visibleField.mount() + + await new Promise(resolve => setTimeout(resolve, 100)) + + const hiddenField = new FieldApi({ + form, + name: 'hiddenField' + }) + hiddenField.mount() + + expect(hiddenField.state.meta.errors).toContain('Hidden field is required') + + expect(visibleField.state.meta.errors).toContain('Visible field is required') + }) + + it('should sync errors for multiple delayed fields', async () => { + const form = new FormApi({ + defaultValues: { + field1: '', + field2: '', + field3: '' + }, + validators: { + onMount: () => { + return { + fields: { + field1: 'Field 1 error', + field2: 'Field 2 error', + field3: 'Field 3 error' + } + } + } + } + }) + + form.mount() + + const field1 = new FieldApi({ form, name: 'field1' }) + field1.mount() + + await new Promise(resolve => setTimeout(resolve, 50)) + + const field2 = new FieldApi({ form, name: 'field2' }) + field2.mount() + + await new Promise(resolve => setTimeout(resolve, 50)) + + const field3 = new FieldApi({ form, name: 'field3' }) + field3.mount() + + expect(field1.state.meta.errors).toContain('Field 1 error') + expect(field2.state.meta.errors).toContain('Field 2 error') + expect(field3.state.meta.errors).toContain('Field 3 error') + }) + + it('should handle field remount scenarios', () => { + const form = new FormApi({ + defaultValues: { + remountField: '' + }, + validators: { + onMount: ({ value }) => { + if (!value.remountField) { + return { fields: { remountField: 'Remount field is required' } } + } + return undefined + } + } + }) + + form.mount() + + const field = new FieldApi({ form, name: 'remountField' }) + const cleanup1 = field.mount() + + expect(field.state.meta.errors).toContain('Remount field is required') + + cleanup1() + + const cleanup2 = field.mount() + expect(field.state.meta.errors).toContain('Remount field is required') + + cleanup2() + }) +}) From 1e84a780a5d0d909e6520654d83ad9c461d677d9 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Thu, 14 Aug 2025 14:57:57 +0900 Subject: [PATCH 2/6] feat: implement form error persistence for delayed-mounted fields --- packages/form-core/src/FieldApi.ts | 22 ++++++++++++++++++++++ packages/form-core/src/FormApi.ts | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index a343bda81..8747bffc1 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1241,6 +1241,28 @@ export class FieldApi< info.instance = this as never this.update(this.options as never) + + const allFieldErrors = this.form.state._allFieldErrors + if (allFieldErrors?.[this.name]) { + const existingErrorMap = allFieldErrors[this.name] + if (existingErrorMap) { + this.setMeta((prev) => ({ + ...prev, + errorMap: { + ...prev.errorMap, + ...existingErrorMap, + }, + errorSourceMap: { + ...prev.errorSourceMap, + ...Object.keys(existingErrorMap).reduce((acc, key) => { + acc[key as keyof typeof acc] = 'form' + return acc + }, {} as Record), + }, + }) as never) + } + } + const { onMount } = this.options.validators || {} if (onMount) { diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 0c62c5cd6..4dea880b1 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -632,6 +632,11 @@ export type BaseFormState< * @private, used to force a re-evaluation of the form state when options change */ _force_re_eval?: boolean + /** + * @private, stores field errors for all fields (including unmounted ones) + * This allows delayed-mounted fields to sync with existing validation errors + */ + _allFieldErrors?: Partial, ValidationErrorMap>> } export type DerivedFormState< @@ -1532,6 +1537,23 @@ export class FormApi< const errorMapKey = getErrorMapKey(validateObj.cause) + if (fieldErrors) { + const allFieldErrors: Partial, ValidationErrorMap>> = this.state._allFieldErrors || {} + for (const [fieldName, fieldError] of Object.entries(fieldErrors)) { + if (fieldError) { + const typedFieldName = fieldName as DeepKeys + allFieldErrors[typedFieldName] = { + ...allFieldErrors[typedFieldName], + [errorMapKey]: fieldError, + } + } + } + this.baseStore.setState((prev) => ({ + ...prev, + _allFieldErrors: allFieldErrors, + })) + } + for (const field of Object.keys( this.state.fieldMeta, ) as DeepKeys[]) { From ed84add280e2bcaea2a84dafd27ee43a191af326 Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Thu, 14 Aug 2025 14:58:55 +0900 Subject: [PATCH 3/6] refactor: rename hidden field mount test file to use .test-d.ts extension --- .../{hidden-field-mount.test.ts => hidden-field-mount.test-d.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/form-core/tests/{hidden-field-mount.test.ts => hidden-field-mount.test-d.ts} (100%) diff --git a/packages/form-core/tests/hidden-field-mount.test.ts b/packages/form-core/tests/hidden-field-mount.test-d.ts similarity index 100% rename from packages/form-core/tests/hidden-field-mount.test.ts rename to packages/form-core/tests/hidden-field-mount.test-d.ts From 10c24afd3cbb8b6ff43f970b1368528575b2698a Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:31:41 +0000 Subject: [PATCH 4/6] ci: apply automated fixes and generate docs --- packages/form-core/src/FieldApi.ts | 34 +++++---- packages/form-core/src/FormApi.ts | 4 +- .../tests/hidden-field-mount.test-d.ts | 74 ++++++++++--------- 3 files changed, 61 insertions(+), 51 deletions(-) diff --git a/packages/form-core/src/FieldApi.ts b/packages/form-core/src/FieldApi.ts index 8747bffc1..5a2915c28 100644 --- a/packages/form-core/src/FieldApi.ts +++ b/packages/form-core/src/FieldApi.ts @@ -1246,20 +1246,26 @@ export class FieldApi< if (allFieldErrors?.[this.name]) { const existingErrorMap = allFieldErrors[this.name] if (existingErrorMap) { - this.setMeta((prev) => ({ - ...prev, - errorMap: { - ...prev.errorMap, - ...existingErrorMap, - }, - errorSourceMap: { - ...prev.errorSourceMap, - ...Object.keys(existingErrorMap).reduce((acc, key) => { - acc[key as keyof typeof acc] = 'form' - return acc - }, {} as Record), - }, - }) as never) + this.setMeta( + (prev) => + ({ + ...prev, + errorMap: { + ...prev.errorMap, + ...existingErrorMap, + }, + errorSourceMap: { + ...prev.errorSourceMap, + ...Object.keys(existingErrorMap).reduce( + (acc, key) => { + acc[key as keyof typeof acc] = 'form' + return acc + }, + {} as Record, + ), + }, + }) as never, + ) } } diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 4dea880b1..22a88a541 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1538,7 +1538,9 @@ export class FormApi< const errorMapKey = getErrorMapKey(validateObj.cause) if (fieldErrors) { - const allFieldErrors: Partial, ValidationErrorMap>> = this.state._allFieldErrors || {} + const allFieldErrors: Partial< + Record, ValidationErrorMap> + > = this.state._allFieldErrors || {} for (const [fieldName, fieldError] of Object.entries(fieldErrors)) { if (fieldError) { const typedFieldName = fieldName as DeepKeys diff --git a/packages/form-core/tests/hidden-field-mount.test-d.ts b/packages/form-core/tests/hidden-field-mount.test-d.ts index a2d0c4713..0b46c08aa 100644 --- a/packages/form-core/tests/hidden-field-mount.test-d.ts +++ b/packages/form-core/tests/hidden-field-mount.test-d.ts @@ -6,7 +6,7 @@ describe('Hidden field mount validation', () => { const form = new FormApi({ defaultValues: { hiddenField: '', - visibleField: '' + visibleField: '', }, validators: { onMount: ({ value }) => { @@ -18,29 +18,31 @@ describe('Hidden field mount validation', () => { errors.visibleField = 'Visible field is required' } return { fields: errors } - } - } + }, + }, }) - + form.mount() - + const visibleField = new FieldApi({ form, - name: 'visibleField' + name: 'visibleField', }) visibleField.mount() - - await new Promise(resolve => setTimeout(resolve, 100)) - + + await new Promise((resolve) => setTimeout(resolve, 100)) + const hiddenField = new FieldApi({ form, - name: 'hiddenField' + name: 'hiddenField', }) hiddenField.mount() - + expect(hiddenField.state.meta.errors).toContain('Hidden field is required') - - expect(visibleField.state.meta.errors).toContain('Visible field is required') + + expect(visibleField.state.meta.errors).toContain( + 'Visible field is required', + ) }) it('should sync errors for multiple delayed fields', async () => { @@ -48,7 +50,7 @@ describe('Hidden field mount validation', () => { defaultValues: { field1: '', field2: '', - field3: '' + field3: '', }, validators: { onMount: () => { @@ -56,28 +58,28 @@ describe('Hidden field mount validation', () => { fields: { field1: 'Field 1 error', field2: 'Field 2 error', - field3: 'Field 3 error' - } + field3: 'Field 3 error', + }, } - } - } + }, + }, }) - + form.mount() - + const field1 = new FieldApi({ form, name: 'field1' }) field1.mount() - - await new Promise(resolve => setTimeout(resolve, 50)) - + + await new Promise((resolve) => setTimeout(resolve, 50)) + const field2 = new FieldApi({ form, name: 'field2' }) field2.mount() - - await new Promise(resolve => setTimeout(resolve, 50)) - + + await new Promise((resolve) => setTimeout(resolve, 50)) + const field3 = new FieldApi({ form, name: 'field3' }) field3.mount() - + expect(field1.state.meta.errors).toContain('Field 1 error') expect(field2.state.meta.errors).toContain('Field 2 error') expect(field3.state.meta.errors).toContain('Field 3 error') @@ -86,7 +88,7 @@ describe('Hidden field mount validation', () => { it('should handle field remount scenarios', () => { const form = new FormApi({ defaultValues: { - remountField: '' + remountField: '', }, validators: { onMount: ({ value }) => { @@ -94,22 +96,22 @@ describe('Hidden field mount validation', () => { return { fields: { remountField: 'Remount field is required' } } } return undefined - } - } + }, + }, }) - + form.mount() - + const field = new FieldApi({ form, name: 'remountField' }) const cleanup1 = field.mount() - + expect(field.state.meta.errors).toContain('Remount field is required') - + cleanup1() - + const cleanup2 = field.mount() expect(field.state.meta.errors).toContain('Remount field is required') - + cleanup2() }) }) From 0171e363bd25e988dd18fde92de837fae1c4121e Mon Sep 17 00:00:00 2001 From: JIHOON LEE Date: Wed, 10 Sep 2025 13:35:22 +0900 Subject: [PATCH 5/6] fix: ensure field errors are properly cleared when removing form fields --- packages/form-core/src/FormApi.ts | 19 +- .../tests/FieldInfo-management.test.ts | 379 ++++++++++++++++++ 2 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 packages/form-core/tests/FieldInfo-management.test.ts diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 22a88a541..4952e07d3 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1556,9 +1556,16 @@ export class FormApi< })) } - for (const field of Object.keys( - this.state.fieldMeta, - ) as DeepKeys[]) { + const allFieldsToProcess = new Set([ + ...Object.keys(this.state.fieldMeta), + ...Object.keys(fieldErrors || {}), + ] as DeepKeys[]) + + for (const field of allFieldsToProcess) { + if (fieldErrors?.[field] && !this.fieldInfo[field]) { + this.getFieldInfo(field) + } + const fieldMeta = this.getFieldMeta(field) if (!fieldMeta) continue @@ -2119,6 +2126,12 @@ export class FormApi< newState.values = deleteBy(newState.values, f) delete this.fieldInfo[f as never] delete newState.fieldMetaBase[f as never] + + if (newState._allFieldErrors?.[f as never]) { + const newAllFieldErrors = { ...newState._allFieldErrors } + delete newAllFieldErrors[f as never] + newState._allFieldErrors = newAllFieldErrors + } }) return newState diff --git a/packages/form-core/tests/FieldInfo-management.test.ts b/packages/form-core/tests/FieldInfo-management.test.ts new file mode 100644 index 000000000..0a91b15bd --- /dev/null +++ b/packages/form-core/tests/FieldInfo-management.test.ts @@ -0,0 +1,379 @@ +import { describe, expect, it } from 'vitest' +import { FieldApi, FormApi } from '../src' + +describe('FieldInfo Management for PR #1691', () => { + describe('Form validation with delayed field mounting', () => { + it('should auto-create fieldInfo entries for fields with validation errors', async () => { + const form = new FormApi({ + defaultValues: { + existingField: '', + delayedField: '', + }, + validators: { + onMount: ({ value }) => { + const errors: Record = {} + if (!value.existingField) { + errors.existingField = 'Existing field is required' + } + if (!value.delayedField) { + errors.delayedField = 'Delayed field is required' + } + return { fields: errors } + }, + }, + }) + + form.mount() + + const existingField = new FieldApi({ + form, + name: 'existingField', + }) + existingField.mount() + + await new Promise((resolve) => setTimeout(resolve, 100)) + + expect(form.fieldInfo.delayedField).toBeDefined() + expect(form.fieldInfo.delayedField.instance).toBeNull() + + const delayedField = new FieldApi({ + form, + name: 'delayedField', + }) + delayedField.mount() + + expect(delayedField.state.meta.errors).toContain('Delayed field is required') + }) + + it('should handle multiple delayed fields with different error types', async () => { + const form = new FormApi({ + defaultValues: { + field1: '', + field2: '', + field3: '', + }, + validators: { + onMount: () => { + return { + fields: { + field1: 'Field 1 error', + field2: 'Field 2 error', + field3: 'Field 3 error', + }, + } + }, + }, + }) + + form.mount() + + await new Promise((resolve) => setTimeout(resolve, 50)) + + expect(form.fieldInfo.field1).toBeDefined() + expect(form.fieldInfo.field2).toBeDefined() + expect(form.fieldInfo.field3).toBeDefined() + + const field1 = new FieldApi({ form, name: 'field1' }) + field1.mount() + + await new Promise((resolve) => setTimeout(resolve, 25)) + + const field2 = new FieldApi({ form, name: 'field2' }) + field2.mount() + + await new Promise((resolve) => setTimeout(resolve, 25)) + + const field3 = new FieldApi({ form, name: 'field3' }) + field3.mount() + + expect(field1.state.meta.errors).toContain('Field 1 error') + expect(field2.state.meta.errors).toContain('Field 2 error') + expect(field3.state.meta.errors).toContain('Field 3 error') + }) + }) + + describe('deleteField functionality', () => { + it('should remove field from fieldInfo and _allFieldErrors', () => { + const form = new FormApi({ + defaultValues: { + fieldToDelete: 'test', + keepField: 'keep', + }, + validators: { + onMount: () => { + return { + fields: { + fieldToDelete: 'Field error', + keepField: 'Keep field error', + }, + } + }, + }, + }) + + form.mount() + + const field = new FieldApi({ + form, + name: 'fieldToDelete', + }) + field.mount() + + expect(form.fieldInfo.fieldToDelete).toBeDefined() + expect(form.state._allFieldErrors?.fieldToDelete).toBeDefined() + + form.deleteField('fieldToDelete') + + expect(form.fieldInfo.fieldToDelete).toBeUndefined() + expect(form.state._allFieldErrors?.fieldToDelete).toBeUndefined() + + expect(form.fieldInfo.keepField).toBeDefined() + expect(form.state._allFieldErrors?.keepField).toBeDefined() + }) + + it('should remove nested fields when parent is deleted', () => { + const form = new FormApi({ + defaultValues: { + parent: { + child1: 'value1', + child2: 'value2', + }, + otherField: 'other', + }, + }) + + form.mount() + + const parentField = new FieldApi({ form, name: 'parent' }) + const child1Field = new FieldApi({ form, name: 'parent.child1' }) + const child2Field = new FieldApi({ form, name: 'parent.child2' }) + const otherField = new FieldApi({ form, name: 'otherField' }) + + parentField.mount() + child1Field.mount() + child2Field.mount() + otherField.mount() + + expect(form.fieldInfo.parent).toBeDefined() + expect(form.fieldInfo['parent.child1']).toBeDefined() + expect(form.fieldInfo['parent.child2']).toBeDefined() + expect(form.fieldInfo.otherField).toBeDefined() + + form.deleteField('parent') + + expect(form.fieldInfo.parent).toBeUndefined() + expect(form.fieldInfo['parent.child1']).toBeUndefined() + expect(form.fieldInfo['parent.child2']).toBeUndefined() + + expect(form.fieldInfo.otherField).toBeDefined() + }) + }) + + describe('removeValue functionality', () => { + it('should remove field value and clean up fieldInfo entry', () => { + const form = new FormApi({ + defaultValues: { + fieldToRemove: 'initial value', + keepField: 'keep value', + }, + }) + + form.mount() + + const field = new FieldApi({ + form, + name: 'fieldToRemove', + }) + const keepField = new FieldApi({ + form, + name: 'keepField', + }) + field.mount() + keepField.mount() + + expect(form.state.values.fieldToRemove).toBe('initial value') + expect(form.fieldInfo.fieldToRemove).toBeDefined() + + form.deleteField('fieldToRemove') + + expect(form.state.values.fieldToRemove).toBeUndefined() + + const fieldInfoKeys = Object.keys(form.fieldInfo) + expect(fieldInfoKeys.includes('fieldToRemove')).toBe(false) + + expect(form.state.values.keepField).toBe('keep value') + expect(form.fieldInfo.keepField).toBeDefined() + }) + + it('should remove field from _allFieldErrors when deleteField is called', () => { + const form = new FormApi({ + defaultValues: { + fieldWithError: '', + otherField: '', + }, + validators: { + onMount: () => { + return { + fields: { + fieldWithError: 'Field error', + otherField: 'Other error', + }, + } + }, + }, + }) + + form.mount() + + const field = new FieldApi({ + form, + name: 'fieldWithError', + }) + field.mount() + + expect(form.state._allFieldErrors?.fieldWithError).toBeDefined() + expect(form.state._allFieldErrors?.otherField).toBeDefined() + + form.deleteField('fieldWithError') + + expect(form.state._allFieldErrors?.fieldWithError).toBeUndefined() + + expect(form.state._allFieldErrors?.otherField).toBeDefined() + }) + }) + + describe('Dynamic field management', () => { + it('should handle dynamic addition and removal of fields', () => { + const form = new FormApi({ + defaultValues: { + dynamicFields: [] as string[], + }, + }) + + form.mount() + + form.setFieldValue('dynamicFields', ['field1', 'field2', 'field3']) + + const field1 = new FieldApi({ form, name: 'dynamicFields[0]' }) + const field2 = new FieldApi({ form, name: 'dynamicFields[1]' }) + const field3 = new FieldApi({ form, name: 'dynamicFields[2]' }) + + field1.mount() + field2.mount() + field3.mount() + + expect(form.fieldInfo['dynamicFields[0]']).toBeDefined() + expect(form.fieldInfo['dynamicFields[1]']).toBeDefined() + expect(form.fieldInfo['dynamicFields[2]']).toBeDefined() + + form.deleteField('dynamicFields[1]') + form.deleteField('dynamicFields[2]') + + const fieldInfoKeys = Object.keys(form.fieldInfo) + expect(fieldInfoKeys.includes('dynamicFields[1]')).toBe(false) + expect(fieldInfoKeys.includes('dynamicFields[2]')).toBe(false) + + expect(form.fieldInfo['dynamicFields[0]']).toBeDefined() + }) + + it('should maintain validation state consistency during field lifecycle', async () => { + const form = new FormApi({ + defaultValues: { + showField: false, + conditionalField: '', + }, + validators: { + onChange: ({ value }) => { + if (value.showField && !value.conditionalField) { + return { + fields: { + conditionalField: 'Conditional field is required when shown', + }, + } + } + return undefined + }, + }, + }) + + form.mount() + + const showFieldApi = new FieldApi({ form, name: 'showField' }) + showFieldApi.mount() + + showFieldApi.setValue(true) + + await new Promise((resolve) => setTimeout(resolve, 50)) + + expect(form.fieldInfo.conditionalField).toBeDefined() + expect(form.state._allFieldErrors?.conditionalField).toBeDefined() + + const conditionalField = new FieldApi({ form, name: 'conditionalField' }) + conditionalField.mount() + + expect(conditionalField.state.meta.errors).toContain( + 'Conditional field is required when shown', + ) + + showFieldApi.setValue(false) + + await new Promise((resolve) => setTimeout(resolve, 50)) + + form.deleteField('conditionalField') + + expect(form.fieldInfo.conditionalField).toBeUndefined() + expect(form.state._allFieldErrors?.conditionalField).toBeUndefined() + }) + }) + + describe('Edge cases and error handling', () => { + it('should handle deleteField on non-existent fields gracefully', () => { + const form = new FormApi({ + defaultValues: { + existingField: 'value', + }, + }) + + form.mount() + + expect(() => { + form.deleteField('nonExistentField' as keyof typeof form.state.values) + }).not.toThrow() + + expect(form.state.values.existingField).toBe('value') + }) + + it('should handle concurrent field operations correctly', async () => { + const form = new FormApi({ + defaultValues: { + field1: 'value1', + field2: 'value2', + field3: 'value3', + }, + }) + + form.mount() + + const field1 = new FieldApi({ form, name: 'field1' }) + const field2 = new FieldApi({ form, name: 'field2' }) + const field3 = new FieldApi({ form, name: 'field3' }) + + field1.mount() + field2.mount() + field3.mount() + + const operations = [ + () => form.deleteField('field1'), + () => form.deleteField('field2'), + () => form.setFieldValue('field3', 'new value'), + ] + + await Promise.all(operations.map((op) => Promise.resolve(op()))) + + expect(form.fieldInfo.field1).toBeUndefined() + expect(form.fieldInfo.field2).toBeUndefined() + expect(form.fieldInfo.field3).toBeDefined() + expect(form.state.values.field3).toBe('new value') + }) + }) +}) From 94dcbd5e4df32a45ab3711d68455e5a7366d66a0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 08:50:31 +0000 Subject: [PATCH 6/6] ci: apply automated fixes and generate docs --- packages/form-core/src/FormApi.ts | 2 +- packages/form-core/tests/FieldInfo-management.test.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 4952e07d3..2030b5aac 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -2126,7 +2126,7 @@ export class FormApi< newState.values = deleteBy(newState.values, f) delete this.fieldInfo[f as never] delete newState.fieldMetaBase[f as never] - + if (newState._allFieldErrors?.[f as never]) { const newAllFieldErrors = { ...newState._allFieldErrors } delete newAllFieldErrors[f as never] diff --git a/packages/form-core/tests/FieldInfo-management.test.ts b/packages/form-core/tests/FieldInfo-management.test.ts index 0a91b15bd..ea10a5a1a 100644 --- a/packages/form-core/tests/FieldInfo-management.test.ts +++ b/packages/form-core/tests/FieldInfo-management.test.ts @@ -42,7 +42,9 @@ describe('FieldInfo Management for PR #1691', () => { }) delayedField.mount() - expect(delayedField.state.meta.errors).toContain('Delayed field is required') + expect(delayedField.state.meta.errors).toContain( + 'Delayed field is required', + ) }) it('should handle multiple delayed fields with different error types', async () => {