like-gold
like-gold1h ago

Cursor Pagination with useInfiniteList in Refine: Still Not Working - Need Help!

I've been stuck on this for days now, and I'm at my wit's end. I'm trying to implement infinite scrolling in a chat application using Refine's useInfiniteList, but the cursor pagination simply refuses to work properly. If anyone has successfully implemented this, please help! What I'm Trying to Do I'm building a WhatsApp-like chat interface where messages should load incrementally as the user scrolls up. Sounds simple enough, right? But the cursor from getNextPageParam is NOT reaching my data provider correctly. The Problem Here's what's happening - my frontend code with useInfiniteList: const { data: messagePages, fetchNextPage, hasNextPage, } = useInfiniteList({ resource: "whatsapp/messages", pagination: { pageSize: 25 }, queryOptions: { getNextPageParam: (lastPage) => { console.log("Last page received:", lastPage); console.log("Cursor next:", lastPage?.cursor?.next); // This shows the cursor perfectly! return lastPage?.cursor?.next ? JSON.stringify(lastPage.cursor.next) : undefined; }, }, }); The console shows: Last page received: {data: Array(25), cursor: {next: {id: "msg_123", timestamp: 1234567890}}} Cursor next: {id: "msg_123", timestamp: 1234567890} Great! But then in my data provider: getList: async ({ resource, pagination, filters, sorters, meta }) => { console.log("[DataProvider] Received pagination:", pagination); // This prints: {currentPage: 1, pageSize: 25, mode: "server"} // WHERE IS MY CURSOR?! 😭 } What I've Already Tried
4 Replies
kapa.ai
kapa.ai1h ago
Hi @Martola. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
like-gold
like-goldOP1h ago
Attempt 1: Following the Documentation The Refine docs mention cursor pagination but the examples don't actually show how the cursor gets passed to the data provider. The GitHub examples seem broken too (found an issue from March 2024 about this). Attempt 2: Checking Different Properties I've tried checking every possible property: const { currentPage, pageSize } = pagination ?? {}; console.log("currentPage type:", typeof currentPage); // Always "number" console.log("pagination.current:", (pagination as any).current); // undefined console.log("pagination.cursor:", (pagination as any).cursor); // undefined console.log("Full pagination object:", JSON.stringify(pagination)); // Just shows: {"currentPage":1,"pageSize":25,"mode":"server"} Attempt 3: Looking at Network Requests The network tab shows my API being called with: GET /api/whatsapp/messages?_skip=0&_take=25 GET /api/whatsapp/messages?_skip=25&_take=25 // When scrolling GET /api/whatsapp/messages?_skip=50&_take=25 // Still no cursor! It's using traditional pagination even though I'm returning a cursor! Attempt 4: Different Return Formats I've tried returning the cursor in different ways from getNextPageParam: // Tried as string return JSON.stringify(lastPage.cursor.next); // Tried as object return lastPage.cursor.next; // Tried encoding return encodeURIComponent(JSON.stringify(lastPage.cursor.next)); // Tried as a simple string ID return lastPage.cursor.next.id; None of them work! The data provider still receives currentPage: 1, 2, 3... My Current Setup Frontend: - Refine version: Latest (installed last week) - React: 18.2.0 - Using Ant Design Backend: - NestJS with Prisma - Returns proper cursor structure: { data: [...messages], total: 1000, cursor: { next: { id: "msg_123", timestamp: 1234567890 }, prev: null } } Questions 1. Has anyone actually gotten cursor pagination working with useInfiniteList? I'm starting to think it might be broken in the current version. 2. Is there a specific format or property name I should use? Maybe Refine expects the cursor in a different structure? 3. Should I just give up and implement my own infinite scroll? I love Refine, but I'm burning too much time on this. 4. Is there a way to bypass Refine's data provider for just this one case? Maybe use React Query directly? What I Need If you've successfully implemented cursor pagination with useInfiniteList, PLEASE share: - Your exact Refine version - How you return the cursor from getNextPageParam - How your data provider receives and processes it - Any workarounds or hacks you had to use I'm desperate here. I've read every piece of documentation, searched through GitHub issues, and even tried reading Refine's source code (got lost pretty quickly). Any help would be massively appreciated. Even if you just have a working example I could look at, that would be amazing. Thanks in advance! 🙏
kapa.ai
kapa.ai1h ago
---------------------- You're running into a common pain point with cursor-based pagination and Refine's useInfiniteList: the cursor value returned by getNextPageParam is not automatically passed to your dataProvider's getList as a cursor parameter. Instead, useInfiniteList only updates the pagination.current value (the "page" number), not a custom cursor object. This is why your dataProvider only sees { current: 1, pageSize: 25, mode: "server" } and not your custom cursor. Why This Happens Refine's useInfiniteList is designed to work generically with many APIs, so by default, it only passes standard pagination parameters (current, pageSize, mode) to your dataProvider. The cursor you return from getNextPageParam is used internally by TanStack Query to manage pagination state, but Refine does not automatically inject it into the pagination or meta parameters sent to your dataProvider. How to Fix It You need to "bridge" the cursor value from getNextPageParam into your dataProvider call. The recommended way is to use the meta property of useInfiniteList to pass the cursor value, and then read it in your dataProvider. Here's how you can do it: 1. Store the cursor in a React state
const [cursor, setCursor] = React.useState(undefined);

const {
data: messagePages,
fetchNextPage,
hasNextPage,
} = useInfiniteList({
resource: "whatsapp/messages",
pagination: { pageSize: 25 },
meta: { cursor }, // Pass the cursor here!
queryOptions: {
getNextPageParam: (lastPage) => {
const nextCursor = lastPage?.cursor?.next;
setCursor(nextCursor); // Update the cursor state
return nextCursor ? JSON.stringify(nextCursor) : undefined;
},
},
});
const [cursor, setCursor] = React.useState(undefined);

const {
data: messagePages,
fetchNextPage,
hasNextPage,
} = useInfiniteList({
resource: "whatsapp/messages",
pagination: { pageSize: 25 },
meta: { cursor }, // Pass the cursor here!
queryOptions: {
getNextPageParam: (lastPage) => {
const nextCursor = lastPage?.cursor?.next;
setCursor(nextCursor); // Update the cursor state
return nextCursor ? JSON.stringify(nextCursor) : undefined;
},
},
});
2. Read the cursor from meta in your dataProvider
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const cursor = meta?.cursor;
console.log("[DataProvider] Received cursor:", cursor);
// Use cursor in your API request as needed
}
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const cursor = meta?.cursor;
console.log("[DataProvider] Received cursor:", cursor);
// Use cursor in your API request as needed
}
Why This Works - The meta property is designed to pass custom data from your hook to your dataProvider, including things like headers, custom query params, or, in this case, a cursor value. This is documented in the Refine docs:
meta is a special property that can be used to pass additional information to data provider methods for the following purposes: Customizing the data provider methods for specific use cases.
See meta usage in useInfiniteList - Refine does not automatically pass the cursor from getNextPageParam to your dataProvider; you must do this manually using meta. Additional Notes - If you want to avoid using React state, you could also use a custom hook or context to manage the cursor, but the above approach is the most direct. - Make sure your dataProvider and backend API are both expecting and handling the cursor parameter correctly. References - How to use cursor-based pagination? (Refine Docs) - meta property in useInfiniteList --- Summary:
You must manually pass the cursor to your dataProvider using the meta property of useInfiniteList. Refine does not do this automatically. This should resolve your issue! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai1h ago
<@434784410907443210> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?