T
TanStack•2y ago
other-emerald

V5: Only previous pages infinite query (chat like infinite scroll)

Hello everyone, In V4 i implemented an infinite list of messages, the previous pages were fetched when scrolling on top, like a chat app, it worked perfectly. I was using this code: (i'm using trpc but i think my issue is more tanstack query related so i post here)
export function useInfinitePrivateMessages(input: InfinitePrivateMessagesInputs, options: { enabled?: boolean }) {
const messagesQuery = trpc.inboxGroup.infinteInboxGroupMessages.useInfiniteQuery(input, {
getPreviousPageParam: (lastPage) => lastPage.prevCursor,
keepPreviousData: true,
...options,
});

return messagesQuery;
}
export function useInfinitePrivateMessages(input: InfinitePrivateMessagesInputs, options: { enabled?: boolean }) {
const messagesQuery = trpc.inboxGroup.infinteInboxGroupMessages.useInfiniteQuery(input, {
getPreviousPageParam: (lastPage) => lastPage.prevCursor,
keepPreviousData: true,
...options,
});

return messagesQuery;
}
But now in v5, getNextPageParams is required. I tried to replace getPreviousPageParam with getNextPageParam and tweak it so i get the same behavior, but i didnt manage to. Everytime i get wrong post order since getNextPageParam is adding new page at the end and i need to add new page at the beginning so i can fetch more recent messages when scrolling top. The only way i managed to get correct order is with this code by using only getPreviousPageParam and bypassing getNextPageParam by always returning undefined :
export function useInfinitePrivateMessages(input: InfinitePrivateMessagesInputs) {
const messagesQuery = trpc.inboxGroup.infinteInboxGroupMessages.useInfiniteQuery(input, {
getNextPageParam: () => undefined,
getPreviousPageParam: (firstPage) => firstPage.prevCursor,
});

return messagesQuery;
}
export function useInfinitePrivateMessages(input: InfinitePrivateMessagesInputs) {
const messagesQuery = trpc.inboxGroup.infinteInboxGroupMessages.useInfiniteQuery(input, {
getNextPageParam: () => undefined,
getPreviousPageParam: (firstPage) => firstPage.prevCursor,
});

return messagesQuery;
}
But when i scroll to get all previous pages and the windows is refocused the content is replaced with only the last previous fetched page. Is it possible in v5 to implements a chat like infinite scroll ? Or i am just bad and maybe someone can share me example ? Thanks you in advance for yout help
15 Replies
ambitious-aqua
ambitious-aqua•2y ago
We made getNextPageParam mandatory because refetches can't work without it. When a refetch happens, we always start at the first page and then fetch forward from there. Wondering how refetching could've worked in v4 without getNextPageParam... I would just implement it as a bidirectional list and just don't allow anything in the UI to fetchNextPage
other-emerald
other-emeraldOP•2y ago
Hmm maybe refetch didnt worked and i did not see it. I was going to do what you suggest but i wasnt sure it was the correct solution. Thanks for your response its more clear now. i think i'm missing something, in bideractional infinite query, how would the endpoint know in which direction it should fetch since hes receiving only a cursor ?
ambitious-aqua
ambitious-aqua•2y ago
You'd need to pass that information
other-emerald
other-emeraldOP•2y ago
I still dont understand something, when refetch happens, my endpoint is called with direction forward (which is ok) but the cursor seems to be the one returned from getPreviousPageParam instead of getNextPageParam 😕
ambitious-aqua
ambitious-aqua•2y ago
can you show a reproduction ?
other-emerald
other-emeraldOP•2y ago
Yep, here is the repro: https://stackblitz.com/edit/tanstack-query-bs7ooo?file=src%2Fpages%2Findex.js To replicate the behavior you can load older message until there is no more and then invalidate the query in devtools. My thinking/understanding is that on refetch the function should be called with getPreviousParams result and direction backward or getNextPageParam result with direction forward but here it is called with getPreviousParam result and direction forward.
brian-bourdon
StackBlitz
Query Load More Infinite Scroll Example (forked) - StackBlitz
Run official live example code for Query Load More Infinite Scroll, created by Tanstack on StackBlitz
ambitious-aqua
ambitious-aqua•2y ago
when a refetch happens, the direction is always "forward", because we start at the first page in the cache and fetch forward from there. and for the first page, we use the pageParam that we have cached, no matter where it came from
should be called with getPreviousParams result and direction backward or getNextPageParam result with direction forward
it doesn't get called with any result of either of these functions, because for the first page, they are not invoked but yeah I can see how this is not actually working because the cursor said "21" because it fetched all items before 21, but now you get all items after 21
ambitious-aqua
ambitious-aqua•2y ago
StackBlitz
Query Load More Infinite Scroll Example - StackBlitz
Run official live example code for Query Load More Infinite Scroll, created by Tanstack on StackBlitz
other-emerald
other-emeraldOP•2y ago
hmm i think i begin to understand the problem, in the tanstack example the first fetched page return a previousId of -5 which is the first item's id of the previous page. It works because the example is simplified and its implemented such that the first item's id of the previous page is predictable. But in real use case, its not predictable, first page could be:
[
{ "name": "Project 0 (server time: 1709755078233)", "id": 114 },
{ "name": "Project 1 (server time: 1709755078233)", "id": 113 },
{ "name": "Project 2 (server time: 1709755078233)", "id": 82 },
{ "name": "Project 3 (server time: 1709755078233)", "id": 80 },
{ "name": "Project 4 (server time: 1709755078233)", "id": 67 }
]
[
{ "name": "Project 0 (server time: 1709755078233)", "id": 114 },
{ "name": "Project 1 (server time: 1709755078233)", "id": 113 },
{ "name": "Project 2 (server time: 1709755078233)", "id": 82 },
{ "name": "Project 3 (server time: 1709755078233)", "id": 80 },
{ "name": "Project 4 (server time: 1709755078233)", "id": 67 }
]
and the previous page:
[
{ "name": "Project 0 (server time: 1709755078233)", "id": 4 },
{ "name": "Project 1 (server time: 1709755078233)", "id": 8 },
{ "name": "Project 2 (server time: 1709755078233)", "id": 12 },
{ "name": "Project 3 (server time: 1709755078233)", "id": 41 },
{ "name": "Project 4 (server time: 1709755078233)", "id": 42 }
]
[
{ "name": "Project 0 (server time: 1709755078233)", "id": 4 },
{ "name": "Project 1 (server time: 1709755078233)", "id": 8 },
{ "name": "Project 2 (server time: 1709755078233)", "id": 12 },
{ "name": "Project 3 (server time: 1709755078233)", "id": 41 },
{ "name": "Project 4 (server time: 1709755078233)", "id": 42 }
]
how would you know that you should return 4 as previous Id ? The only solution i can see is literally fetching the whole previous page just to return the id of the first item as previousId.
ambitious-aqua
ambitious-aqua•2y ago
yeah I'm not sure. I think one way to fix it is for us to store the original direction next to pages and pageParams, then pass that as direction to the first refetch. But it's quite a lot on our end. Do you see another way?
other-emerald
other-emeraldOP•2y ago
so in this case will the first refetch have the direction "backward" and the others "forward" or will they all have the direction "backward" ?
ambitious-aqua
ambitious-aqua•2y ago
Only the first one would have backward
other-emerald
other-emeraldOP•2y ago
Ok, I see. I think this should work. And to answer your previous question, after some thought, I don't really see other solution without changing a lot about how infinite query refetches works.
ambitious-aqua
ambitious-aqua•2y ago
please file an issue but I'm not really sure yet if a fix is possible in a backwards compatible way
other-emerald
other-emeraldOP•2y ago
finally found some time to file an issue, here it is: https://github.com/TanStack/query/issues/7203
GitHub
Refetching bidirectional infinite query skipping first page · Issue...
Describe the bug I'm filling this issue after some discussions on the tanstack discord about this problem. I will try my best to explain it but the easiest way to understand it is with an examp...

Did you find this page helpful?