Skip to content

fix: bump array version on reset so array fields re-render - #2243

Open
hemraj-007 wants to merge 1 commit into
TanStack:mainfrom
hemraj-007:fix/array-reset-rerender-2228
Open

fix: bump array version on reset so array fields re-render#2243
hemraj-007 wants to merge 1 commit into
TanStack:mainfrom
hemraj-007:fix/array-reset-rerender-2228

Conversation

@hemraj-007

@hemraj-007 hemraj-007 commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Test Plan

  • pnpm run test:lib in packages/form-core
  • pnpm run test:lib in packages/react-form

Summary by CodeRabbit

  • Bug Fixes
    • Fixed array-based form fields not re-rendering correctly after resetting the form or an individual field.
    • Array fields now immediately reflect changes in length and updated values after a reset.

Bump _arrayVersion after form.reset and form.resetField so React array
fields re-render when reset shortens the array.

Fixes TanStack#2228

Co-authored-by: Cursor <cursoragent@cursor.com>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Form reset operations now bump _arrayVersion for array fields, ensuring array-mode adapters re-render when reset values change array length. Core and React tests cover form resets to shorter arrays, and a changeset records patch releases.

Changes

Array reset rerender

Layer / File(s) Summary
Reset array metadata updates
packages/form-core/src/FormApi.ts, .changeset/fix-array-reset-rerender.md
reset() and resetField() bump array metadata versions after applying array values; release metadata records the fix and patch releases.
Array reset regression coverage
packages/form-core/tests/FormApi.spec.ts, packages/react-form/tests/useField.test.tsx
Tests verify shortened reset arrays update _arrayVersion, field values, and rendered list entries.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • TanStack/form#2172: Uses the same _arrayVersion mechanism for array swap and move behavior.
  • TanStack/form#2190: Uses array-version bumps for updates involving async default values.

Suggested reviewers: crutchcorn

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning It summarizes the fix and tests, but it omits the required Changes, Checklist, and Release Impact sections. Add the missing template sections with the change summary, checklist checkboxes, and whether a changeset/release impact applies.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main fix: bumping array version on reset so array fields re-render.
Linked Issues check ✅ Passed The reset and resetField logic now bumps _arrayVersion for array fields, matching #2228 and backed by tests.
Out of Scope Changes check ✅ Passed The changeset and tests support the array reset fix and release workflow, so no unrelated scope is evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/form-core/src/FormApi.ts (1)

1850-1858: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract duplicated array-version-bump loop into a shared helper.

The loop at lines 1851–1858 is identical to the one already in update() at lines 1786–1793. Extracting it into a private method (e.g., bumpArrayVersionsForAllFields()) would eliminate the duplication and ensure both call sites stay in sync.

♻️ Proposed refactor
+  /**
+   * `@private` Bump _arrayVersion for every mounted field whose value is an array.
+   */
+  private bumpArrayVersionsForAllFields = () => {
+    const helper = metaHelper(this)
+    for (const fieldKey of Object.keys(
+      this.fieldInfo,
+    ) as DeepKeys<TFormData>[]) {
+      if (Array.isArray(this.getFieldValue(fieldKey))) {
+        helper.bumpArrayVersion(fieldKey)
+      }
+    }
+  }
+
   reset = (values?: TFormData, opts?: { keepDefaultValues?: boolean }) => {
     // ... existing reset logic ...
 
-    const helper = metaHelper(this)
-    for (const fieldKey of Object.keys(
-      this.fieldInfo,
-    ) as DeepKeys<TFormData>[]) {
-      if (Array.isArray(this.getFieldValue(fieldKey))) {
-        helper.bumpArrayVersion(fieldKey)
-      }
-    }
+    this.bumpArrayVersionsForAllFields()
   }

Then apply the same replacement in update() at lines 1786–1793.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/form-core/src/FormApi.ts` around lines 1850 - 1858, Extract the
duplicated array-version-bump loop from update() and the shown flow into a
shared private method, such as bumpArrayVersionsForAllFields(). Have the helper
create or use metaHelper(this), iterate over fieldInfo keys, and call
bumpArrayVersion for array-valued fields; replace both existing loops with calls
to this method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/form-core/src/FormApi.ts`:
- Around line 2966-2969: Add a FormApi.spec.ts test covering resetField on an
array field, using the existing FormApi, FieldApi, and resetField test patterns.
Set an array value, call resetField, then assert the default array is restored
and the field meta _arrayVersion is bumped as expected.

---

Nitpick comments:
In `@packages/form-core/src/FormApi.ts`:
- Around line 1850-1858: Extract the duplicated array-version-bump loop from
update() and the shown flow into a shared private method, such as
bumpArrayVersionsForAllFields(). Have the helper create or use metaHelper(this),
iterate over fieldInfo keys, and call bumpArrayVersion for array-valued fields;
replace both existing loops with calls to this method.
🪄 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: b94af9cf-baaa-470c-9593-93e75079298b

📥 Commits

Reviewing files that changed from the base of the PR and between 5d11281 and b59f247.

📒 Files selected for processing (4)
  • .changeset/fix-array-reset-rerender.md
  • packages/form-core/src/FormApi.ts
  • packages/form-core/tests/FormApi.spec.ts
  • packages/react-form/tests/useField.test.tsx

Comment on lines +2966 to +2969

if (Array.isArray(this.getFieldValue(field))) {
metaHelper(this).bumpArrayVersion(field)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add test coverage for resetField array version bump.

The reset() path has a dedicated test in FormApi.spec.ts, but the resetField() array-version bump (lines 2967–2968) has no corresponding test. A regression could silently break this path.

🧪 Suggested test
it('should bump array version when resetField is called on an array field', () => {
  const form = new FormApi({
    defaultValues: {
      items: [1, 2, 3],
    },
  })
  form.mount()

  const field = new FieldApi({ form, name: 'items' })
  field.mount()

  form.setFieldValue('items', [1, 2, 3, 4, 5])
  // _arrayVersion is bumped by pushFieldValue or setFieldValue paths

  form.resetField('items')

  expect(form.getFieldValue('items')).toEqual([1, 2, 3])
  expect(form.getFieldMeta('items')?._arrayVersion).toBe(1)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/form-core/src/FormApi.ts` around lines 2966 - 2969, Add a
FormApi.spec.ts test covering resetField on an array field, using the existing
FormApi, FieldApi, and resetField test patterns. Set an array value, call
resetField, then assert the default array is restored and the field meta
_arrayVersion is bumped as expected.

@MILLERMARRU MILLERMARRU left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went to check why bumping _arrayVersion specifically is what's needed here, and it's because react-form's useField deliberately subscribes to state.meta._arrayVersion instead of state.value for mode="array" fields, there's already a comment there referencing #1925 explaining it's an intentional optimization to avoid re-rendering on every child property change. That means for an array field, React genuinely does not know the array changed unless _arrayVersion itself changes, no matter what happened to the actual array reference or length. form.reset()/resetField() changing the array without touching that counter is exactly the kind of thing that selector is blind to, so the fix is targeting the right mechanism, not just papering over a symptom.

Checked the ordering in both call sites too. In reset(), the new loop runs after the baseStore.setState that actually commits the new values, so this.getFieldValue(fieldKey) inside the loop is reading the post-reset array, not stale state. Same story in resetField.

One minor thing, not a correctness issue: resetField isn't wrapped in batch(), so setting fieldMetaBase[field] to defaultFieldMeta (which resets _arrayVersion to 0) and then calling bumpArrayVersion right after are two separate store commits rather than one. Since the array value itself is already correct as of the first commit, this shouldn't produce wrong output, just possibly one extra render pass in cases where _arrayVersion wasn't already 0 going in. Worth a look if this file already batches similar multi-step meta+value updates elsewhere, but not something I'd block on.

Good that the React test renders through an actual mode="array" field and checks the DOM list length after reset, rather than asserting on internal state, since the whole point of the fix is what the UI shows, not just what the store holds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Resetting array fields does not trigger full re-render

2 participants