Skip to content

fix(ui): keep the list drawer mounted while its page changes - #19432

Open
alphanull wants to merge 1 commit into
payloadcms:mainfrom
alphanull:fix/ui-list-drawer-stay-mounted
Open

alphanull wants to merge 1 commit into
payloadcms:mainfrom
alphanull:fix/ui-list-drawer-stay-mounted

Conversation

@alphanull

Copy link
Copy Markdown

What?

Keep an open list drawer mounted when the user changes pages or when filterOptions gets a new object identity. The drawer shell should stay put; only the list inside it should swap.

Why?

useListDrawer memoizes the drawer component with filterOptions in its dependency list. Upload fields build that object in useMemo, and a new identity makes React treat MemoizedDrawer as a new component type. The open drawer unmounts and its enter animation runs again.

Paging has the same visible result for a different reason. ListDrawerContent wraps the list in Suspense and renders .drawer__fade-in only while isLoading is false. A synchronous setListView of the next server-rendered list suspends into the fallback, unmounts that node, and the 200ms fade-in runs again. The drawer looks like it closes and reopens on every page, instead of just replacing the rows.

We hit this in an upload relationship drawer ("choose from existing") with a large image collection. It still happens with no custom list view installed. It is much more obvious in production, where the server render is slow enough for the unmount to be visible. In dev the swap is often fast enough that the animation is easy to miss.

How?

  • Read filterOptions through a ref inside the memoized drawer, and drop it from the useMemo dependency list, so a new object does not recreate the component.
  • Commit setListView inside startTransition. The previous list stays on screen until the next one can paint, so the drawer does not fall through the Suspense fallback and remount .drawer__fade-in.

Reproduction (manual)

  1. Open a document with an upload field that relates to a collection with more than one page of files.
  2. Choose "from existing" so the list drawer opens.
  3. Change pages.
  4. Without this change the drawer fades out and back in on every page. With it, the drawer stays open and only the list updates.

Paging rendered the next list in a transition that suspended into the drawer fallback, and a new filterOptions object recreated the drawer component. Both remount the open drawer and replay its enter animation.
@coderabbitai

coderabbitai Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

ListDrawer now reads the latest filterOptions without recreating its memoized component when those options change. It also applies list-view and loading-state updates in transitions when a list render returns or the collection slug is absent.

Changes: ListDrawer updates

Layer / File(s) Summary
Keep filter options current
packages/ui/src/elements/ListDrawer/index.tsx
useListDrawer stores the latest filterOptions in a ref and passes that value to MemoizedDrawer. filterOptions is no longer a dependency of its useMemo call.
Transition list and loading updates
packages/ui/src/elements/ListDrawer/DrawerContent.tsx
List-view and loading-state updates run inside startTransition when a list render returns or the collection slug is absent.

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

Change: Bug fix

Possibly related PRs

  • payloadcms/payload#19005: Adds a Suspense fallback around ListDrawerContent; this PR changes how list updates are applied during pagination.

Suggested reviewers: jessrynkar

Merge Risk: Low · up to 19916

The change keeps the drawer from flickering when paging. As a side effect, an open drawer can keep showing rows from an outdated filter if a sibling field's filter updates late. Closing and reopening the drawer fixes it, so the impact is narrow.

Pre-merge checks: Passed 5

Check name Status Explanation
Title check Passed The title identifies the main change: keeping the list drawer mounted during page changes.
Description check Passed The description matches the changes to drawer memoization and list transitions.
Docstring Coverage Passed No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentat…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

No eligible functions found in the changed files. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 0 functions across 2 files.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Tip

🎯 Customize your risk assessments

Guide how CodeRabbit assesses merge risk and security architecture risk. Highlight sensitive changes and explain your security boundaries using Merge Risk Instructions and Security Risk Instructions in your review settings. Security architecture risk assessments require the Advanced plan or above.

✅ Automatic approvals, with your rules

Let CodeRabbit approve pull requests once required threads are resolved, pre-merge checks pass, and your risk limits are met. Enable Automatic approvals in review settings, with overrides for teams or file paths.

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

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between 25dfb24 and 19916df.

Files selected for processing: 2

  • packages/ui/src/elements/ListDrawer/DrawerContent.tsx
  • packages/ui/src/elements/ListDrawer/index.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

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

Development

Successfully merging this pull request may close these issues.

1 participant