How to handle cursor-based pagination in React-Admin?
Asked Answered
T

1

6

My API provides cursor-based pagination not offset-limit pagination, so is hard to match up to the page: {int} and perPage: {int} configuration that React-Admin provides for GET_LIST and GET_MANY_REFERENCE.

Cursor-based pagination is used by many APIs with fast-moving data like Twitter, Facebook and Slack.

At its simplest cursor-based pagination means that a list response includes (either in response body or headers) opaque cursors that are 'bookmarks' to the end of the previous page and beginning of the next page. It may or not include cursors for every resource in the response, and may or not include persistent cursors for the first and last available pages. A cursor-based paginated API may well also not be able to provide a meaningful 'total count' of the resources matching the provided filter, as currently insisted upon in the react-admin GET_LIST and GET_MANY_REFERENCE responses.

Similar questions seem to have been asked on React-Admin issue tracker (react-admin#1510 and react-admin#1787) and on Stack-Overflow How to paginate react-admin lists when the total is unknown?) but all the answers seem to be quite fragile workarounds.

To be specific about the request/response idiom supported by my API, an initial request might be like so:

GET http://example.com/api/resources?<filter-params>&paging.limit=10

The response includes up to the requested 10 resources, and cursors that mark the beginning and end of this page:

X-Paging-Since: <since-cursor>
X-Paging-Until: <until-cursor>

[ {...}, {...}, ... ]

The previous page is then:

GET http://example.com/api/resources?<filter-params>&paging.until=<since-cursor>

and the next page is:

GET http://example.com/api/resources?<filter-params>&paging.since=<until-cursor>

(In this API, those complete URLs for next/prev (and first/last) are actually available in the Link response headers also: Link: <prev-url>;rel=prev, <next-url>;rel=next, <first-url>;rel=first, <last-url>;rel=last.)

Obviously my data provider could reformat these headers into the data returned to react-admin however we want, but it's not clear how best to wire up to the react-admin List or Pagination components, or the Pagination component to pass the appropriate cursor/URL back to the data provider for the next request.

Turbid answered 21/10, 2019 at 9:40 Comment(3)
Catch the custom headers in your dataProvider after the initial GET_LIST, pass the paging parameters through to the data response. Use a custom reducer as described in https://mcmap.net/q/1918978/-how-to-paginate-react-admin-lists-when-the-total-is-unknown to store the paging position in redux. On your next GET_LIST call, grab the paging results out of redux and format them to your API. This isn't fragile as it uses react-admin's existing redux system, stores state where it should be storedStealthy
@HarleyAlexander - how would you suggest going about accessing the redux store when you say 'On your next GET_LIST call, grab the paging results out of redux and format them to your API'. I can't see how I can do this within my custom buildQuery functionAlight
@Alight I also have the same question. I was able to update the paging results via a custom reducer, i.e. if (type == CRUD_GET_LIST_SUCCESS) { return payload.lastid } else if (type == CRUD_GET_LIST) { if (previousState !== null ) { payload.pagination['lastid'] = previousState }}. My problem is, the previous pages aren't cached. For example, when I click prev, another call is made to getList which ends up using the previous the lastid and pages forward. I'll continue investigating and update this as I go.Coterminous
W
0

Since react-admin version 4, pagination has been improved to support partial pagination:

https://marmelab.com/react-admin/DataProviderWriting.html#partial-pagination https://marmelab.com/react-admin/ListTutorial.html#building-a-custom-pagination

Wish answered 12/5, 2022 at 18:44 Comment(1)
Thanks for sharing. This looks like it could help but with only Boolean values and not cursors in the pageInfo object it doesn't seem to do enough.Turbid

© 2022 - 2024 — McMap. All rights reserved.