fix(ui): re-evaluate filterOptions on subsequent drawer opens for relationship fields#16131
Open
lcnogueira wants to merge 5 commits intopayloadcms:mainfrom
Open
Conversation
…lookup in DrawerContent
…anges Use a ref to hold the latest filterOptions in useListDrawer so that updates to filterOptions (e.g. after selecting a value) do not recreate MemoizedDrawer. A new function reference causes React to unmount and remount ListDrawerContent, resetting selectedOption state — which broke the polymorphic relationship list drawer filter.
0791ec1 to
0f06c30
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What?
When a
relationshipfield usesadmin.appearance: 'drawer'with afilterOptionsfunction that depends on sibling field values, the filter stops being applied after the user selects a value and reopens the drawer. This is specific to the drawer appearance — the defaultselectappearance works correctly.Why?
Three bugs compound to produce the issue:
Bug 1 — ListView caching (
DrawerContent.tsx)DrawerContentcaches the rendered server list inListViewstate and only callsrefresh()(which re-fetches with the currentfilterOptions) whenListViewis null. On first open,ListViewis null so the fetch runs correctly. On any subsequent open,ListViewis already set, sorefresh()is skipped and the stale cached list is shown — meaningfilterOptionsis never re-evaluated against the updated form state.Bug 2 —
idfilter overwrite (Input.tsx)listDrawerFilterOptionscombines the user'sfilterOptionsresult with anot_inexclusion for already-selected values using object spread. When both produce anidcondition (e.g.id: { not_equals: sibling1Id }fromfilterOptionsandid: { not_in: [selectedId] }from the exclusion logic), the spread silently overwrites the user's condition. This means the user's filter is dropped from the server query once a value is selected in the field.Bug 3 —
MemoizedDrawerremount onfilterOptionschange (index.tsx)MemoizedDrawerinuseListDrawerhadfilterOptionsin itsuseMemodependency array. After a value is selected,listDrawerFilterOptionsinInput.tsxproduces a new object reference (with the selected value excluded vianot_in). This causesMemoizedDrawerto receive a new function reference, which React treats as a new component type — unmounting and remountingListDrawerContent. This resets itsselectedOptionstate back to the first collection, breaking the polymorphic drawer filter entirely.How?
Fix 1 — Clear
ListViewon drawer closeAdded an effect that resets
ListViewtoundefinedand restores the loading state whenever the drawer closes. This ensures every subsequent open triggers a freshrefresh()call with the latestfilterOptions.Also added an
isOpenguard to the existing!ListVieweffect to prevent a wasted server round-trip whenrefreshis recreated as a side effect ofisOpenchanging to false.Fix 2 — Merge
idconditions withandlogicReplaced the object spread in
listDrawerFilterOptionswithhoistQueryParamsToAnd(already used elsewhere in the codebase) so bothidconditions are preserved:Fix 3 — Stabilize
MemoizedDrawerwith a refUsed a
useRefto hold the latestfilterOptionsvalue, updated synchronously during render. The ref itself (notref.current) is captured in the memoized component, so each render reads the current value without recreating the function.filterOptionsis removed from theuseMemodependency array, preventing remounts:Fixes #15971