fix: revalidate nested fields after parent object updates#2125
fix: revalidate nested fields after parent object updates#2125marc-niclas wants to merge 1 commit intoTanStack:mainfrom
Conversation
- Fixes the stale nested field meta described in TanStack#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 TanStack#2113
📝 WalkthroughWalkthroughThis PR adds a patch release to Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsTimed out fetching pipeline failures after 30000ms Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/form-core/src/FormApi.ts (1)
2287-2297: Defer descendant lookup until validation is enabled.
descendantFieldsis only used insideif (!dontValidate), but this scan now runs on everysetFieldValuecall. That makes array helpers in this file pay an unnecessary O(field count) walk even when they explicitly skip validation first.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/src/FormApi.ts` around lines 2287 - 2297, The current eager computation of descendantFields (using fieldString and this.fieldInfo) runs on every setFieldValue call even when validation is skipped; move the descendantFields calculation and any dependent logic into the block guarded by if (!dontValidate) so the Object.keys(this.fieldInfo).filter(...) scan only runs when validation is enabled, keeping fieldString creation and descendant lookup deferred until needed (ensure any references to descendantFields are updated accordingly inside that block).packages/form-core/tests/FormApi.spec.ts (1)
228-260: Add an array-path regression for the bracket branch too.The implementation now has separate descendant matching for object paths (
a.b) and array paths (a[0].b), but this test only covers the dot-path case. A smallusers[0].namevariant would keep the newfieldString + '['branch covered as well.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/form-core/tests/FormApi.spec.ts` around lines 228 - 260, Add a parallel test that covers the array-path branch: duplicate the "should clear nested field errors when setting a parent object field" case but use an array-style path (e.g. defaultValues: { users: [{ name: '' }] } and FieldApi name 'users[0].name' or defaultValues: { a: [{ b: 0 }] } and FieldApi name 'a[0].b'), mount FormApi and FieldApi, trigger the validator to produce an error (setValue to a failing value), then call form.setFieldValue on the parent array object (e.g. setFieldValue('users', [{ name: 'ok' }] ) or setFieldValue('a', [{ b: 1 }])) and assert the child field value updates and that childField.state.meta.errors is cleared and childField.state.meta.isValid is true to cover the bracket-path branch in FieldApi/FormApi.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/form-core/src/FormApi.ts`:
- Around line 2328-2330: The loop calling
this.getFieldInfo(descendantField).instance?.validate('change') causes N
repeated form-level validations because FieldApi.validate triggers form
sync/async validation; replace this direct per-descendant validate call with a
single helper on FormApi (e.g., a refreshDescendants or queueDescendantChanges)
that collects descendant field keys and calls the form-level validation once or
uses the existing form-level validation runner to revalidate all affected fields
in one pass; modify the code path around descendantFields iteration in FormApi
to call that helper instead of FieldApi.validate, ensuring you reuse the
form-level validation runner (the same routine used by FieldApi.validate) to
avoid abort/restart churn.
---
Nitpick comments:
In `@packages/form-core/src/FormApi.ts`:
- Around line 2287-2297: The current eager computation of descendantFields
(using fieldString and this.fieldInfo) runs on every setFieldValue call even
when validation is skipped; move the descendantFields calculation and any
dependent logic into the block guarded by if (!dontValidate) so the
Object.keys(this.fieldInfo).filter(...) scan only runs when validation is
enabled, keeping fieldString creation and descendant lookup deferred until
needed (ensure any references to descendantFields are updated accordingly inside
that block).
In `@packages/form-core/tests/FormApi.spec.ts`:
- Around line 228-260: Add a parallel test that covers the array-path branch:
duplicate the "should clear nested field errors when setting a parent object
field" case but use an array-style path (e.g. defaultValues: { users: [{ name:
'' }] } and FieldApi name 'users[0].name' or defaultValues: { a: [{ b: 0 }] }
and FieldApi name 'a[0].b'), mount FormApi and FieldApi, trigger the validator
to produce an error (setValue to a failing value), then call form.setFieldValue
on the parent array object (e.g. setFieldValue('users', [{ name: 'ok' }] ) or
setFieldValue('a', [{ b: 1 }])) and assert the child field value updates and
that childField.state.meta.errors is cleared and childField.state.meta.isValid
is true to cover the bracket-path branch in FieldApi/FormApi.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eb54d83b-a972-4b48-8d63-ec34ddefb943
📒 Files selected for processing (3)
.changeset/stale-nested-field-meta.mdpackages/form-core/src/FormApi.tspackages/form-core/tests/FormApi.spec.ts
| descendantFields.forEach((descendantField) => { | ||
| this.getFieldInfo(descendantField).instance?.validate('change') | ||
| }) |
There was a problem hiding this comment.
This revalidates the whole form once per descendant.
FieldApi.validate() also runs form sync/async validation (packages/form-core/src/FieldApi.ts:1904-1932), so a single parent update now fans out into repeated form revalidation and async abort/restart churn. On large nested forms or async onChange validators, that can turn one change into N+1 form validations. Please route descendant refresh through a helper that reuses the single form-level pass instead of calling instance.validate('change') directly.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/form-core/src/FormApi.ts` around lines 2328 - 2330, The loop calling
this.getFieldInfo(descendantField).instance?.validate('change') causes N
repeated form-level validations because FieldApi.validate triggers form
sync/async validation; replace this direct per-descendant validate call with a
single helper on FormApi (e.g., a refreshDescendants or queueDescendantChanges)
that collects descendant field keys and calls the form-level validation once or
uses the existing form-level validation runner to revalidate all affected fields
in one pass; modify the code path around descendantFields iteration in FormApi
to call that helper instead of FieldApi.validate, ensuring you reuse the
form-level validation runner (the same routine used by FieldApi.validate) to
avoid abort/restart churn.
🎯 Changes
Fixes the stale nested field meta described in #2113.
When
setFieldValueupdates a parent object field likea, mounted descendant fields likea.bare 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
afrom{ b: 0 }to{ b: 1 }should clear the existing validation error ona.b.✅ Checklist
pnpm test:pr.🚀 Release Impact
Summary by CodeRabbit
Bug Fixes