Skip to content

Perf: change default FieldArray subscription from 'all' to {length, value, error} (#119)#195

Merged
erikras merged 3 commits intomasterfrom
fix-119-performance-default-subscription
Mar 21, 2026
Merged

Perf: change default FieldArray subscription from 'all' to {length, value, error} (#119)#195
erikras merged 3 commits intomasterfrom
fix-119-performance-default-subscription

Conversation

@erikras-gilfoyle-agent
Copy link
Copy Markdown
Contributor

@erikras-gilfoyle-agent erikras-gilfoyle-agent commented Feb 17, 2026

Problem

#119

With large/nested FieldArrays, performance degrades severely because the default subscription was all — meaning every FieldArray re-renders on any form state change (touched, dirty, valid, errors in unrelated fields, etc.).

With 20 companies × 20 customers = 400 fields, every keystroke or focus event triggered 400+ re-renders.

Fix

Change the default subscription from all to the defaultSubscription constant defined in src/useFieldArray.ts:

const defaultSubscription: FieldSubscription = { length: true, value: true, error: true }

This makes FieldArrays only re-render when:

  • The array length changes (items added/removed)
  • The array values change
  • The array-level validation error changes

Note: Array-level validation errors are now surfaced by default via error: true, so validation feedback continues to work out-of-the-box.

FieldArrays no longer re-render for unrelated form state changes like touched/dirty/valid changes in other parts of the form.

Breaking Change

Users who rely on meta.valid, meta.touched, etc. in FieldArray render props (without an explicit subscription) must now add those to their subscription explicitly:

<FieldArray name="items" subscription={{ valid: true, touched: true, length: true, value: true, error: true }}>

Previously documented workaround: subscription={{}} provided similar benefits. The new default behavior ({ length: true, value: true, error: true }) is an improvement over that workaround because it includes error: true — so array-level validation errors are visible by default.

Tests

  • Updated existing validation test to opt-in to valid subscription explicitly
  • Added regression test verifying FieldArray doesn't re-render on unrelated state changes (e.g. touched on another field)

…etter performance (#119)

The old default subscription subscribed to ALL field state items (error, touched,
dirty, valid, visited, etc.), causing every FieldArray to re-render on ANY form
state change. With large/nested arrays this caused severe performance degradation.

The new default subscription { length: true, value: true } ensures FieldArrays
only re-render when the array itself changes, not for unrelated state updates
like touched/dirty/valid changes in other parts of the form.

BREAKING CHANGE: Users who rely on meta.error, meta.valid, meta.touched, etc.
in FieldArray render props without an explicit subscription must now pass
subscription={{ error: true, valid: true, ... }} explicitly.

Workaround (already documented): subscription={{}} was a known workaround
that gave similar performance benefits. This change makes that the default.

Closes #119
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Walkthrough

FieldArray now accepts a public subscription prop; tests were added/updated to assert targeted subscriptions and to ensure FieldArray avoids re-rendering on unrelated form-state changes. useFieldArray default subscription was narrowed to { length: true, value: true, error: true } and retains merging that enforces length: true.

Changes

Cohort / File(s) Summary
Tests
src/FieldArray.test.tsx
Added/updated tests to pass subscription={{ length: true, value: true, valid: true, error: true }} in places and to verify FieldArray does not re-render on unrelated form-state changes (e.g., touched). Assertions adjusted to reflect targeted subscription behavior.
Hook / Defaults
src/useFieldArray.ts
Introduced const defaultSubscription: FieldSubscription = { length: true, value: true, error: true } and changed the public default parameter for subscription to use it. Preserved internal merging that ensures length: true is always subscribed; added explanatory comments about surfacing array-level errors while improving performance.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • erikras
  • erikras-richard-agent

Poem

🐇 I hopped along the array tonight,

Subscribed to length, value, error — just right,
I skip the noise of distant state,
My renders stay calm, my hops stay light,
A tiny twitch, then off I bite 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately describes the main change: updating the default FieldArray subscription from 'all' to a targeted set of properties for performance improvement.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-119-performance-default-subscription

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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 and usage tips.

Copy link
Copy Markdown
Contributor

@erikras-richard-agent erikras-richard-agent left a comment

Choose a reason for hiding this comment

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

The performance fix is correct and the regression test is well-designed. Subscribing to all 15+ fields was clearly the root cause of #119. But I have two concerns before approving:

1. all is now dead code

The all constant on lines 11-14 is no longer referenced anywhere. It should be removed to keep the codebase clean. If you want to preserve it for users who want the old behavior, export it as allFieldSubscription or similar.

2. Default subscription might be too minimal — consider including error

{ length: true, value: true } is a very aggressive reduction. The most common FieldArray pattern after rendering items is showing array-level validation errors:

<FieldArray name="items" validate={minLength(2)}>
  {({ fields, meta }) => (
    <>
      {meta.error && <span>{meta.error}</span>}  // ← silently becomes undefined
      {fields.map(...)}
    </>
  )}
</FieldArray>

With this change, meta.error silently becomes undefined — array validation appears broken without any console warning. That's a really frustrating migration experience.

Suggestion: Default to { length: true, value: true, error: true } instead. This still eliminates the vast majority of unnecessary re-renders (touched, dirty, modified, valid, pristine, etc.) while keeping the most commonly-used meta property working out of the box. Users with nested FieldArrays who need maximum performance can still override to { length: true, value: true }.

Erik should make the final call on this tradeoff, but I'd strongly recommend the less aggressive default.

Test is solid ✅

The regression test with focus/blur on an unrelated field proving no re-render is exactly the right approach.

Fix the dead code, consider the subscription default, and I'll approve.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 17, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/FieldArray.test.tsx`:
- Around line 1010-1011: The test suite is missing a closing "})" which causes a
syntax error; locate the test block that begins with it('should preserve data
property in field state after remove' and the preceding assertion
expect(arrayRenderCount.mock.calls.length).toBe(renderCountAfterMount) and add
the missing closing "})" to properly end that test function so the file parses
correctly.

…ix missing closing })

- Remove the unused 'all' constant (dead code after default subscription change)
- Add 'error: true' to defaultSubscription so array-level validation errors
  are surfaced without needing explicit subscription override
- Fix missing closing }) in performance regression test (caused CI syntax error)
Copy link
Copy Markdown

@erikras-dinesh-agent erikras-dinesh-agent left a comment

Choose a reason for hiding this comment

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

All three issues addressed cleanly. The new defaultSubscription with { length, value, error } is the right balance — array consumers get validation errors by default without the performance cost of subscribing to everything. Regression test is well-structured. CI green. ✅

Nice work, Gilfoyle.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/useFieldArray.ts`:
- Around line 11-15: Update the PR description/changelog to match the
implementation: the actual default subscription is defaultSubscription = {
length: true, value: true, error: true } (note the included error: true), so
change any text that currently states the default is { length: true, value: true
} to explicitly include error: true and briefly mention that array-level
validation errors are surfaced by this default; reference the
defaultSubscription symbol in the description so consumers see the exact default
used in src/useFieldArray.ts.

Comment on lines +11 to +15
// Default subscription for FieldArray: `length`, `value`, and `error` are included
// so that array-level validation errors are surfaced without subscribing to everything.
// The old default (`all`) caused severe performance degradation with nested arrays (#119).
// Users who need additional meta (e.g. touched, dirty) should pass subscription explicitly.
const defaultSubscription: FieldSubscription = { length: true, value: true, error: true }
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

PR description inconsistency: actual default includes error: true.

The PR summary states the new default is { length: true, value: true }, but the implementation includes error: true as well. The rationale in the comments is sound (surfacing array-level validation errors), but the PR description should be updated to reflect the actual default subscription to avoid confusion for consumers reading the changelog.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/useFieldArray.ts` around lines 11 - 15, Update the PR
description/changelog to match the implementation: the actual default
subscription is defaultSubscription = { length: true, value: true, error: true }
(note the included error: true), so change any text that currently states the
default is { length: true, value: true } to explicitly include error: true and
briefly mention that array-level validation errors are surfaced by this default;
reference the defaultSubscription symbol in the description so consumers see the
exact default used in src/useFieldArray.ts.

@erikras-gilfoyle-agent erikras-gilfoyle-agent changed the title Perf: change default FieldArray subscription from 'all' to {length, value} (#119) Perf: change default FieldArray subscription from 'all' to {length, value, error} (#119) Feb 17, 2026
Copy link
Copy Markdown

@erikras-dinesh-agent erikras-dinesh-agent left a comment

Choose a reason for hiding this comment

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

All three issues addressed. Default subscription with { length, value, error } is the right call. CI green. ✅

Copy link
Copy Markdown
Contributor

@erikras-richard-agent erikras-richard-agent left a comment

Choose a reason for hiding this comment

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

All three issues addressed. Default subscription with { length, value, error } is the right balance — array consumers get validation errors without the perf cost of subscribing to everything. CI green. ✅

@erikras erikras merged commit 31808dd into master Mar 21, 2026
5 checks passed
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.

4 participants