Skip to content

Commit 115895f

Browse files
authored
feat(useInfiniteQuery): respect cancelRefetch flag for infinite queries (fetchNextPage, fetchPreviousPage) (TanStack#2731)
1 parent ec65515 commit 115895f

File tree

5 files changed

+95
-2
lines changed

5 files changed

+95
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ stats-hydration.json
3333
stats-react.json
3434
stats.html
3535
.vscode/settings.json
36+
.idea/

docs/src/pages/reference/useInfiniteQuery.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,13 @@ The returned properties for `useInfiniteQuery` are identical to the [`useQuery`
5555
- `fetchNextPage: (options?: FetchNextPageOptions) => Promise<UseInfiniteQueryResult>`
5656
- This function allows you to fetch the next "page" of results.
5757
- `options.pageParam: unknown` allows you to manually specify a page param instead of using `getNextPageParam`.
58+
- `options.cancelRefetch: boolean` if set to `true`, calling `fetchNextPage` repeatedly will invoke `fetchPage` every time, whether the previous
59+
invocation has resolved or not. Also, the result from previous invocations will be ignored. If set to `false`, calling `fetchNextPage`
60+
repeatedly won't have any effect until the first invocation has resolved. Default is `true`.
5861
- `fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise<UseInfiniteQueryResult>`
5962
- This function allows you to fetch the previous "page" of results.
6063
- `options.pageParam: unknown` allows you to manually specify a page param instead of using `getPreviousPageParam`.
64+
- `options.cancelRefetch: boolean` same as for `fetchNextPage`.
6165
- `hasNextPage: boolean`
6266
- This will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option).
6367
- `hasPreviousPage: boolean`

src/core/infiniteQueryObserver.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ export class InfiniteQueryObserver<
9494
options?: FetchNextPageOptions
9595
): Promise<InfiniteQueryObserverResult<TData, TError>> {
9696
return this.fetch({
97-
cancelRefetch: true,
97+
// TODO consider removing `?? true` in future breaking change, to be consistent with `refetch` API (see https://github.com/tannerlinsley/react-query/issues/2617)
98+
cancelRefetch: options?.cancelRefetch ?? true,
9899
throwOnError: options?.throwOnError,
99100
meta: {
100101
fetchMore: { direction: 'forward', pageParam: options?.pageParam },
@@ -106,7 +107,8 @@ export class InfiniteQueryObserver<
106107
options?: FetchPreviousPageOptions
107108
): Promise<InfiniteQueryObserverResult<TData, TError>> {
108109
return this.fetch({
109-
cancelRefetch: true,
110+
// TODO consider removing `?? true` in future breaking change, to be consistent with `refetch` API (see https://github.com/tannerlinsley/react-query/issues/2617)
111+
cancelRefetch: options?.cancelRefetch ?? true,
110112
throwOnError: options?.throwOnError,
111113
meta: {
112114
fetchMore: { direction: 'backward', pageParam: options?.pageParam },

src/core/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,10 +282,12 @@ export interface ResetOptions {
282282
}
283283

284284
export interface FetchNextPageOptions extends ResultOptions {
285+
cancelRefetch?: boolean
285286
pageParam?: unknown
286287
}
287288

288289
export interface FetchPreviousPageOptions extends ResultOptions {
290+
cancelRefetch?: boolean
289291
pageParam?: unknown
290292
}
291293

src/react/tests/useInfiniteQuery.test.tsx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -747,6 +747,90 @@ describe('useInfiniteQuery', () => {
747747
})
748748
})
749749

750+
it('should silently cancel an ongoing fetchNextPage request when another fetchNextPage is invoked', async () => {
751+
const key = queryKey()
752+
const start = 10
753+
const fetchPage = jest.fn(async ({ pageParam = start }) => {
754+
await sleep(50)
755+
return Number(pageParam)
756+
})
757+
758+
function Page() {
759+
const { fetchNextPage } = useInfiniteQuery(key, fetchPage, {
760+
getNextPageParam: lastPage => lastPage + 1,
761+
})
762+
763+
React.useEffect(() => {
764+
setActTimeout(() => {
765+
fetchNextPage()
766+
}, 100)
767+
setActTimeout(() => {
768+
fetchNextPage()
769+
}, 110)
770+
}, [fetchNextPage])
771+
772+
return null
773+
}
774+
775+
renderWithClient(queryClient, <Page />)
776+
777+
await sleep(300)
778+
779+
expect(fetchPage).toBeCalledTimes(3)
780+
expect(fetchPage).toHaveBeenNthCalledWith(1, {
781+
pageParam: undefined,
782+
queryKey: [key],
783+
})
784+
expect(fetchPage).toHaveBeenNthCalledWith(2, {
785+
pageParam: 11,
786+
queryKey: [key],
787+
})
788+
expect(fetchPage).toHaveBeenNthCalledWith(3, {
789+
pageParam: 11,
790+
queryKey: [key],
791+
})
792+
})
793+
794+
it('should not cancel an ongoing fetchNextPage request when another fetchNextPage is invoked if `cancelRefetch: false` is used ', async () => {
795+
const key = queryKey()
796+
const start = 10
797+
const fetchPage = jest.fn(async ({ pageParam = start }) => {
798+
await sleep(50)
799+
return Number(pageParam)
800+
})
801+
802+
function Page() {
803+
const { fetchNextPage } = useInfiniteQuery(key, fetchPage, {
804+
getNextPageParam: lastPage => lastPage + 1,
805+
})
806+
807+
React.useEffect(() => {
808+
setActTimeout(() => {
809+
fetchNextPage()
810+
}, 100)
811+
setActTimeout(() => {
812+
fetchNextPage({ cancelRefetch: false })
813+
}, 110)
814+
}, [fetchNextPage])
815+
816+
return null
817+
}
818+
819+
renderWithClient(queryClient, <Page />)
820+
821+
await sleep(300)
822+
823+
expect(fetchPage).toBeCalledTimes(2)
824+
expect(fetchPage).toHaveBeenNthCalledWith(1, {
825+
pageParam: undefined,
826+
queryKey: [key],
827+
})
828+
expect(fetchPage).toHaveBeenNthCalledWith(2, {
829+
pageParam: 11,
830+
queryKey: [key],
831+
})
832+
})
833+
750834
it('should keep fetching first page when not loaded yet and triggering fetch more', async () => {
751835
const key = queryKey()
752836
const states: UseInfiniteQueryResult<number>[] = []

0 commit comments

Comments
 (0)