Skip to content

fix(ui): avoid mounting collapsed array fields - #18240

Open
AbarnaaSree wants to merge 1 commit into
payloadcms:mainfrom
AbarnaaSree:fix/18182-array-row-performance
Open

AbarnaaSree wants to merge 1 commit into
payloadcms:mainfrom
AbarnaaSree:fix/18182-array-row-performance

Conversation

@AbarnaaSree

Copy link
Copy Markdown

What?

Fixes the Array field performance issues reported in #18182.

This PR addresses two sources of unnecessary rendering:

  • Collapsed Array rows were still mounting all of their fields.
  • Array row labels were subscribing to the whole form watch context, causing them to rerender on unrelated form changes.

Why?

When an Array contains many rows, the number of mounted fields can grow significantly even when most rows are collapsed.

Collapsible hides its children visually, but the children remain mounted. As a result, ArrayRow was still rendering RenderFields for collapsed rows.

Additionally, RowLabelProvider was using useWatchForm(). Since the form watch context changes when the form updates, row labels could rerender on every field change, even when the change was unrelated to the row label.

Together, these behaviors can increase rendering work and contribute to typing latency as the number of Array rows grows.

How?

1. Do not mount fields for collapsed Array rows

Updated packages/ui/src/fields/Array/ArrayRow.tsx so that RenderFields is only rendered when the row is expanded.

The loading state is unchanged and still renders ShimmerEffect.

Before:

{isLoading ? (
  <ShimmerEffect />
) : (
  <RenderFields
    ...
  />
)}

After:

{isLoading ? (
  <ShimmerEffect />
) : row.collapsed ? null : (
  <RenderFields
    ...
  />
)}

This prevents the fields inside collapsed rows from being mounted while keeping the existing expanded-row behavior.

2. Avoid subscribing RowLabelProvider to FormWatchContext

Updated packages/ui/src/forms/RowLabel/Context/index.tsx to use useForm() instead of useWatchForm().

Before:

const { getDataByPath, getSiblingData } = useWatchForm()

After:

const { getDataByPath, getSiblingData } = useForm()

This prevents RowLabelProvider from subscribing to the form's watch context and avoids rerenders caused by unrelated form updates.

3. Add regression tests

Added tests covering both changes:

  • packages/ui/src/fields/Array/ArrayRow.spec.ts

    • Verifies RenderFields is not mounted when the row is collapsed.
    • Verifies RenderFields is mounted when the row is expanded.
  • packages/ui/src/forms/RowLabel/Context/index.spec.ts

    • Verifies row label data is read from FormContext.
    • Verifies FormWatchContext is not used for retrieving the row data.

Testing

Focused Vitest tests were run:

Test Files  2 passed (2)
Tests       3 passed (3)

Also verified:

git diff --check

with no errors.

Related Issue

Fixes #18182

This branch has not been deployed

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

Labels

None yet

Projects

None yet

1 participant