Skip to content

Commit da0d427

Browse files
committed
fix: revalidate nested fields after parent object updates
- Fixes the stale nested field meta described in #2113. - When `setFieldValue` updates a parent object field like `a`, mounted descendant fields like `a.b` are now revalidated on change as well. This keeps nested field-level errors in sync after programmatic parent object updates. - Added a regression test covering the reported case where updating `a` from `{ b: 0 }` to `{ b: 1 }` should clear the existing validation error on `a.b`. Fixes #2113
1 parent a4e8dec commit da0d427

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/form-core': patch
3+
---
4+
5+
Fix stale nested field errors when setting a parent object field by revalidating mounted descendant fields after the parent value changes.

packages/form-core/src/FormApi.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,17 @@ export class FormApi<
22842284
const dontUpdateMeta = opts?.dontUpdateMeta ?? false
22852285
const dontRunListeners = opts?.dontRunListeners ?? false
22862286
const dontValidate = opts?.dontValidate ?? false
2287+
const fieldString = field.toString()
2288+
const descendantFields = Object.keys(this.fieldInfo).filter((fieldKey) => {
2289+
if (fieldKey === fieldString) {
2290+
return false
2291+
}
2292+
2293+
return (
2294+
fieldKey.startsWith(`${fieldString}.`) ||
2295+
fieldKey.startsWith(`${fieldString}[`)
2296+
)
2297+
}) as DeepKeys<TFormData>[]
22872298

22882299
batch(() => {
22892300
if (!dontUpdateMeta) {
@@ -2313,6 +2324,10 @@ export class FormApi<
23132324

23142325
if (!dontValidate) {
23152326
this.validateField(field, 'change')
2327+
2328+
descendantFields.forEach((descendantField) => {
2329+
this.getFieldInfo(descendantField).instance?.validate('change')
2330+
})
23162331
}
23172332
}
23182333

packages/form-core/tests/FormApi.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,40 @@ describe('form api', () => {
225225
expect(form.getFieldValue('name')).toEqual('other')
226226
})
227227

228+
it('should clear nested field errors when setting a parent object field', () => {
229+
const form = new FormApi({
230+
defaultValues: {
231+
a: {
232+
b: 0,
233+
},
234+
},
235+
})
236+
237+
const childField = new FieldApi({
238+
form,
239+
name: 'a.b',
240+
validators: {
241+
onChange: ({ value }) =>
242+
value > 0 ? undefined : 'Must be greater than 0',
243+
},
244+
})
245+
246+
form.mount()
247+
childField.mount()
248+
249+
childField.setValue(0)
250+
251+
expect(childField.state.meta.errors).toEqual(['Must be greater than 0'])
252+
253+
form.setFieldValue('a', {
254+
b: 1,
255+
})
256+
257+
expect(form.getFieldValue('a.b')).toBe(1)
258+
expect(childField.state.meta.errors).toEqual([])
259+
expect(childField.state.meta.isValid).toBe(true)
260+
})
261+
228262
it("should be dirty after a field's value has been set", () => {
229263
const form = new FormApi({
230264
defaultValues: {

0 commit comments

Comments
 (0)