Skip to content

<PrevNextButtons storeKey={false}> has no effect #11370

Description

@papppeter

What you were expecting:

The <PrevNextButtons> documentation types storeKey as string | false and describes it as "The key to use to match a filter & sort configuration of a <List>. Pass false to disable."

I expected storeKey={false} to make the pager ignore the stored list params for the resource, so that it pages using only the sort, filter and filterDefaultValues given as props — the same meaning storeKey={false} has on <List>.

The use case: a <Show> view for a message that belongs to a conversation. The prev/next buttons should walk that conversation (filter={{ thread }}, chronological sort) no matter how the user last filtered or sorted the global message list.

What happened instead:

storeKey={false} has no effect — it behaves exactly like omitting the prop. The pager still reads <resource>.listParams from the store, so the list's stored filter is merged into the query and the list's stored sort order overrides the sort prop.

Same page, same props, only storeKey differing:

storeKey={false}
  GET /messages?order[displayDate]=desc&page=1&itemsPerPage=1000&direction=outbound&thread=11

storeKey="messages.threadPager"   (a key nothing ever writes)
  GET /messages?order[displayDate]=asc&page=1&itemsPerPage=1000&thread=11

With storeKey={false} the direction=outbound filter — stored earlier by the list — leaks in, and order=desc from the store wins over the sort={{ field: 'displayDate', order: 'ASC' }} prop.

Steps to reproduce:

  1. Add <PrevNextButtons storeKey={false} sort={{ field: 'last_name', order: 'ASC' }} /> to a <Show> or <Edit> view's actions.
  2. Open the corresponding list and apply a filter and a different sort (e.g. sort by first_name DESC), so params are written to the store.
  3. Open a record's Show/Edit view and watch the getList call the pager issues.
  4. Observed: the call carries the list's stored filter, sorted by first_name DESC. Expected: no filter, sorted by last_name ASC.

Related code:

storeKey is destructured and then used only in the useStore key expression, where false falls through to the default key:

// packages/ra-core/src/controller/usePrevNextController.ts
const {
    linkType = 'edit',
    storeKey,
    limit = 1000,
    sort: initialSort = { field: 'id', order: SORT_ASC },
    filter: permanentFilter = {},
    filterDefaultValues = {},
    ...
} = props;

const [storedParams] = useStore<ListParams>(
    storeKey || `${resource}.listParams`,   // `false` → `${resource}.listParams`
    {
        filter: filterDefaultValues,
        order: initialSort.order,
        sort: initialSort.field,
        page: 1,
        perPage: 10,
        displayedFilters: {},
    }
);

const [storedParams] = useStore<ListParams>(
storeKey || `${resource}.listParams`,
{
filter: filterDefaultValues,
order: initialSort.order,
sort: initialSort.field,
page: 1,
perPage: 10,
displayedFilters: {},
}
);

storedParams is then used unconditionally to build the sort and filter passed to getList, so there is no later branch where storeKey === false is honoured — storeKey appears exactly three times in the file: the destructuring, the useStore key, and the type declaration.

Other information:

  • useListParams has the same storeKey || \${resource}.listParams`expression, but handlesfalseby ignoring the resulting value downstream, with an explicit comment: *"As we can't conditionally call a hook, if the storeKey is false, we'll ignore the params variable later on and won't call setParams either."*usePrevNextController` has no equivalent.
  • <List storeKey={false}> got its full "disable all store interactions" treatment in Disable all store interactions for a list with storeKey set to false (sort, pagination, filters and now also selection state). #9742. It looks like usePrevNextController was simply never updated to match.
  • The docs cover this in the props table only; the ## storeKey section documents the string case alone.
  • Present since <PrevNextButtons> was introduced — the same three lines appear in v4.14.0, v4.16.20, v5.0.0 and current master (4e469da).
  • Suggested fix, mirroring useListParams: keep calling useStore under the default key (hooks can't be conditional), but ignore storedParams when storeKey === false and fall back to the props-derived defaults. Happy to open a PR if that approach suits you.
  • Workaround for anyone hitting this: pass a string key that nothing else ever writes (e.g. storeKey="messages.threadPager"), which gives the intended "use only my props" behaviour.

Environment

  • React-admin version: 5.15.3
  • Last version that did not exhibit the issue (if applicable): none — present since 4.14.0, the release that introduced the component
  • React version: 19.3.0
  • Browser: Chromium 141 (not browser-specific)
  • Stack trace (in case of a JS error): n/a — no error is raised; the query is silently wrong

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions