From e7169e6846c3673c8adad3cb026fae894174336b Mon Sep 17 00:00:00 2001 From: Fenet Date: Mon, 12 Oct 2026 21:09:04 +0300 Subject: [PATCH] fix(form-core): handle null field defaults --- .changeset/null-field-default-value.md | 5 ++++ packages/form-core/src/FormApi.ts | 11 ++++--- packages/form-core/tests/FieldApi.spec.ts | 36 +++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 .changeset/null-field-default-value.md diff --git a/.changeset/null-field-default-value.md b/.changeset/null-field-default-value.md new file mode 100644 index 0000000000..69aa54c0ff --- /dev/null +++ b/.changeset/null-field-default-value.md @@ -0,0 +1,5 @@ +--- +'@tanstack/form-core': patch +--- + +Treat field-level `defaultValue: null` as a valid default value instead of falling back to the form-level default when resolving `isDefaultValue` and in `resetField`. diff --git a/packages/form-core/src/FormApi.ts b/packages/form-core/src/FormApi.ts index 4400017d30..4ce7512137 100644 --- a/packages/form-core/src/FormApi.ts +++ b/packages/form-core/src/FormApi.ts @@ -1233,11 +1233,13 @@ export class FormApi< // As primitives, we don't need to aggressively persist the same referential value for performance reasons const isFieldValid = !isNonEmptyArray(fieldErrors) const isFieldPristine = !currBaseMeta.isDirty + const fieldDefault = + this.getFieldInfo(fieldName).instance?.options.defaultValue const isDefaultValue = evaluate( curFieldVal, - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - this.getFieldInfo(fieldName)?.instance?.options.defaultValue ?? - getBy(this.options.defaultValues, fieldName), + fieldDefault !== undefined + ? fieldDefault + : getBy(this.options.defaultValues, fieldName), ) if ( @@ -2940,7 +2942,8 @@ export class FormApi< const fieldDefault = this.getFieldInfo(field).instance?.options.defaultValue const formDefault = getBy(this.options.defaultValues, field) - const targetValue = fieldDefault ?? formDefault + const targetValue = + fieldDefault !== undefined ? fieldDefault : formDefault return { ...prev, diff --git a/packages/form-core/tests/FieldApi.spec.ts b/packages/form-core/tests/FieldApi.spec.ts index 7c9a468ab8..8aceeb8952 100644 --- a/packages/form-core/tests/FieldApi.spec.ts +++ b/packages/form-core/tests/FieldApi.spec.ts @@ -132,6 +132,42 @@ describe('field api', () => { expect(field.getMeta().isDefaultValue).toBe(true) }) + it('should treat field-level defaultValue of null as default when form default is non-null', () => { + const form = new FormApi({ + defaultValues: { + name: 'test' as string | null, + }, + }) + form.mount() + + const field = new FieldApi({ + form, + name: 'name', + defaultValue: null, + }) + field.mount() + + expect(field.getValue()).toBe(null) + expect(field.getMeta().isDefaultValue).toBe(true) + + field.setValue('other') + expect(field.getMeta().isDefaultValue).toBe(false) + + field.setValue(null) + expect(field.getMeta().isDefaultValue).toBe(true) + + form.resetField('name') + expect(field.getValue()).toBe(null) + expect(field.getMeta().isDefaultValue).toBe(true) + + field.setValue('other') + expect(field.getMeta().isDefaultValue).toBe(false) + + form.reset() + expect(field.getValue()).toBe(null) + expect(field.getMeta().isDefaultValue).toBe(true) + }) + it('should be false when value is undefined and a default value is specified in form-level only', () => { const form = new FormApi({ defaultValues: {