React-query: No way to configure useInfiniteQuery to concatenate pages in correct order when refetching stale query

Created on 24 Sep 2020  路  10Comments  路  Source: tannerlinsley/react-query

Describe the bug
When using fetchMore(..., { previous: true }) to load a previous page and prepend it, if you then perform an action that causes ReactQuery to refetch, the requests are performed in the correct order but are appended instead of prepended. Currently there doesn't seem to be a way to let ReactQuery know how it should reconstruct the refetched data.

To Reproduce
Steps to reproduce the behavior:

  1. Go to this CodeSandbox which is a modified fork from the Infinite Loading example from the docs.
  2. Click on "Load more" a few times until no more messages can be loaded.
  3. Scroll down and click on "Go to another page".
  4. Click on "Back" to return to messages.
  5. See that the query is refetched, but now the messages are displayed in an incorrect order.

Expected behavior
If fetchMore(..., { previous: true }) is meant for infinite queries with a reversed direction, then useInfiniteQuery should accept some kind of option to indicate how background refetches should be handled.

Additional context
I may be misunderstanding the purpose of the previous feature. If that's the case then maybe additional details could be added to the docs to make it's purpose clearer. Nonetheless, I would definitely recommend adding an appendMode option where the direction of the list can be configured. This will allow ReactQuery's useInfiniteQuery to be used for reversed lists (such as chat messages, activity feeds, and the like).

PS: Issue #434 is related, and a possible solution seems to have been in development at some point, but I'm not sure if it ever got implemented or merged and the issue is now closed.

Thank you so much for the wonderful library! 馃檪

bug

All 10 comments

This is an interesting issue for sure. Totally makes sense. We'll see what we can do about it!

Thanks, @tannerlinsley! Let me know if there's any way I can help.

I've been thinking about it overnight, and I've visualized two possible solutions:

Option A

_Add an option to the query config where the direction of appended pages can be specified._


Possible names for the option

Since naming is hard 馃槄, here are some suggestions I thought:

Key names:

  • appendMode
  • pageMergeMode
  • pagesDirection
  • fetchMoreDirection

Value names:

  • 'append' | 'prepend'
  • 'forward' | 'backward'
  • 'previous' | 'next'

Sometimes if no name seems simple or intuitive, it may indicate it's the wrong solution.

One limitation of this solution is that it doesn't really handle all the possible uses of fetchMore(..., { previous: boolean }). If a list is rendered at a middle point, and there are both "fetch previous" and "fetch next" buttons, there's no guaranteed order. So the API would still allow a user to implement a series of queries that won't be faithfully reproduced on refetches.

Option B

_Make useInfiniteQuery remember the specific calls to fetchMore that triggered the current list of queries._

In theory, this could allow for fetching more data in any direction, but I can imagine it may complicate the query's logic quite a bit.


Hope this helps to come up with the right solution for ReactQuery.

I don't think Option A would work as well as we would need it to. For option B, I could see that happening. We would also need to record which order they were fetched in if we were to reproduce the "serial" nature of it, eg. next, next, next, prev, next, prev could change root cursors over time and thus, you would need to execute that specific order serially again to ensure that the same windowing is achieved, but with accurate cursors.

Come to think of it, if all we did was store that sequence, along with the pagination information that was used to fetch it, it would work.

@tannerlinsley Agree, that's sort of what I had in mind for option B, but you explain it much better.

One thing I just realized is that even with option B, there's going to be a disconnect between the canFetchMore boolean and bi-directional infinite lists since it will apply to one direction only.

If you check out lines 40 and 60 of index.js in the sandbox I shared above, you can see that the fetchMore has to commit to a specific direction. Btw, right now for a previous direction we have to duplicate the next-cursor logic in two places.

Something else to have in mind.

Would be nice if we can get this fixed in V3. Think we have two options here:

  1. Remove the previous option from fetchMore and add a reverse/prepend/direction query option to set a fixed direction.
  2. Have a way to support both directions at the same time:
 const {
  fetchPreviousPage,
  fetchNextPage,
  hasPreviousPage,
  hasNextPage,
  isFetchingPreviousPage,
  isFetchingNextPage,
 } = useInfiniteQuery(queryKey, queryFn, {
  getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,
  getNextPageParam: (lastPage, allPages) => lastPage.nextCursor
 })

Although in your example we are not loading "previous" pages, we are loading the next page but the list is displayed in reverse order. So maybe even with option 2 it would be valuable to have a reverse option.

I think we should support it. The current "previous" functionality, although broken, represents the needs of at least a few individuals in the history of the library that voiced enough practical need to try and build it.

Bi-directional navigation and reversed lists are now supported in v3!
https://react-query-beta.tanstack.com/guides/infinite-queries

Just wanted to say that the implemented solution looks perfect! Thank you so much @boschni and @tannerlinsley for considering this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mickykebe picture mickykebe  路  3Comments

ggascoigne picture ggascoigne  路  4Comments

benediktviebahn picture benediktviebahn  路  4Comments

tiagofernandez picture tiagofernandez  路  3Comments

bgazzera picture bgazzera  路  4Comments