Skip to content

Commit 8ce6642

Browse files
jasongerbesTkDodo
andauthored
feat(query-core): improve useInfiniteQuery error handling (#7418)
Co-authored-by: Dominik Dorfmeister <[email protected]>
1 parent 7499ba8 commit 8ce6642

File tree

6 files changed

+693
-14
lines changed

6 files changed

+693
-14
lines changed

docs/framework/react/reference/useInfiniteQuery.md

+11-6
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ The options for `useInfiniteQuery` are identical to the [`useQuery` hook](../use
5454

5555
**Returns**
5656

57-
The returned properties for `useInfiniteQuery` are identical to the [`useQuery` hook](../useQuery), with the addition of the following and a small difference in `isRefetching`:
57+
The returned properties for `useInfiniteQuery` are identical to the [`useQuery` hook](../useQuery), with the addition of the following properties and a small difference in `isRefetching` and `isRefetchError`:
5858

5959
- `data.pages: TData[]`
6060
- Array containing all pages.
@@ -66,19 +66,24 @@ The returned properties for `useInfiniteQuery` are identical to the [`useQuery`
6666
- Will be `true` while fetching the previous page with `fetchPreviousPage`.
6767
- `fetchNextPage: (options?: FetchNextPageOptions) => Promise<UseInfiniteQueryResult>`
6868
- This function allows you to fetch the next "page" of results.
69-
`getNextPageParam`.
70-
- `options.cancelRefetch: boolean` if set to `true`, calling `fetchNextPage` repeatedly will invoke `fetchPage` every time, whether the previous
69+
- `options.cancelRefetch: boolean` if set to `true`, calling `fetchNextPage` repeatedly will invoke `queryFn` every time, whether the previous
7170
invocation has resolved or not. Also, the result from previous invocations will be ignored. If set to `false`, calling `fetchNextPage`
7271
repeatedly won't have any effect until the first invocation has resolved. Default is `true`.
7372
- `fetchPreviousPage: (options?: FetchPreviousPageOptions) => Promise<UseInfiniteQueryResult>`
7473
- This function allows you to fetch the previous "page" of results.
7574
- `options.cancelRefetch: boolean` same as for `fetchNextPage`.
7675
- `hasNextPage: boolean`
77-
- This will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option).
76+
- Will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option).
7877
- `hasPreviousPage: boolean`
79-
- This will be `true` if there is a previous page to be fetched (known via the `getPreviousPageParam` option).
78+
- Will be `true` if there is a previous page to be fetched (known via the `getPreviousPageParam` option).
79+
- `isFetchNextPageError: boolean`
80+
- Will be `true` if the query failed while fetching the next page.
81+
- `isFetchPreviousPageError: boolean`
82+
- Will be `true` if the query failed while fetching the previous page.
8083
- `isRefetching: boolean`
81-
- Is `true` whenever a background refetch is in-flight, which _does not_ include initial `pending` or fetching of next or previous page
84+
- Will be `true` whenever a background refetch is in-flight, which _does not_ include initial `pending` or fetching of next or previous page
8285
- Is the same as `isFetching && !isPending && !isFetchingNextPage && !isFetchingPreviousPage`
86+
- `isRefetchError: boolean`
87+
- Will be `true` if the query failed while refetching a page.
8388

8489
Keep in mind that imperative fetch calls, such as `fetchNextPage`, may interfere with the default refetch behaviour, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like `hasNextPage && !isFetching`.

packages/query-core/src/__tests__/infiniteQueryObserver.test-d.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ describe('InfiniteQueryObserver', () => {
5151
expectTypeOf(result.data).toEqualTypeOf<InfiniteData<string, unknown>>()
5252
expectTypeOf(result.error).toEqualTypeOf<Error>()
5353
expectTypeOf(result.status).toEqualTypeOf<'error'>()
54+
expectTypeOf(result.isFetchNextPageError).toEqualTypeOf<false>()
55+
expectTypeOf(result.isFetchPreviousPageError).toEqualTypeOf<false>()
5456
}
5557

5658
if (result.isSuccess) {

packages/query-core/src/infiniteQueryObserver.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
FetchNextPageOptions,
1111
FetchPreviousPageOptions,
1212
InfiniteData,
13+
InfiniteQueryObserverBaseResult,
1314
InfiniteQueryObserverOptions,
1415
InfiniteQueryObserverResult,
1516
QueryKey,
@@ -145,26 +146,33 @@ export class InfiniteQueryObserver<
145146
>,
146147
): InfiniteQueryObserverResult<TData, TError> {
147148
const { state } = query
148-
const result = super.createResult(query, options)
149+
const parentResult = super.createResult(query, options)
149150

150-
const { isFetching, isRefetching } = result
151+
const { isFetching, isRefetching, isError, isRefetchError } = parentResult
152+
const fetchDirection = state.fetchMeta?.fetchMore?.direction
151153

152-
const isFetchingNextPage =
153-
isFetching && state.fetchMeta?.fetchMore?.direction === 'forward'
154+
const isFetchNextPageError = isError && fetchDirection === 'forward'
155+
const isFetchingNextPage = isFetching && fetchDirection === 'forward'
154156

155-
const isFetchingPreviousPage =
156-
isFetching && state.fetchMeta?.fetchMore?.direction === 'backward'
157+
const isFetchPreviousPageError = isError && fetchDirection === 'backward'
158+
const isFetchingPreviousPage = isFetching && fetchDirection === 'backward'
157159

158-
return {
159-
...result,
160+
const result: InfiniteQueryObserverBaseResult<TData, TError> = {
161+
...parentResult,
160162
fetchNextPage: this.fetchNextPage,
161163
fetchPreviousPage: this.fetchPreviousPage,
162164
hasNextPage: hasNextPage(options, state.data),
163165
hasPreviousPage: hasPreviousPage(options, state.data),
166+
isFetchNextPageError,
164167
isFetchingNextPage,
168+
isFetchPreviousPageError,
165169
isFetchingPreviousPage,
170+
isRefetchError:
171+
isRefetchError && !isFetchNextPageError && !isFetchPreviousPageError,
166172
isRefetching:
167173
isRefetching && !isFetchingNextPage && !isFetchingPreviousPage,
168174
}
175+
176+
return result as InfiniteQueryObserverResult<TData, TError>
169177
}
170178
}

packages/query-core/src/types.ts

+59
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,13 @@ export interface ResultOptions {
473473
}
474474

475475
export interface RefetchOptions extends ResultOptions {
476+
/**
477+
* If set to `true`, a currently running request will be cancelled before a new request is made
478+
*
479+
* If set to `false`, no refetch will be made if there is already a request running.
480+
*
481+
* Defaults to `true`.
482+
*/
476483
cancelRefetch?: boolean
477484
}
478485

@@ -486,10 +493,26 @@ export interface InvalidateOptions extends RefetchOptions {}
486493
export interface ResetOptions extends RefetchOptions {}
487494

488495
export interface FetchNextPageOptions extends ResultOptions {
496+
/**
497+
* If set to `true`, calling `fetchNextPage` repeatedly will invoke `queryFn` every time,
498+
* whether the previous invocation has resolved or not. Also, the result from previous invocations will be ignored.
499+
*
500+
* If set to `false`, calling `fetchNextPage` repeatedly won't have any effect until the first invocation has resolved.
501+
*
502+
* Defaults to `true`.
503+
*/
489504
cancelRefetch?: boolean
490505
}
491506

492507
export interface FetchPreviousPageOptions extends ResultOptions {
508+
/**
509+
* If set to `true`, calling `fetchPreviousPage` repeatedly will invoke `queryFn` every time,
510+
* whether the previous invocation has resolved or not. Also, the result from previous invocations will be ignored.
511+
*
512+
* If set to `false`, calling `fetchPreviousPage` repeatedly won't have any effect until the first invocation has resolved.
513+
*
514+
* Defaults to `true`.
515+
*/
493516
cancelRefetch?: boolean
494517
}
495518

@@ -712,15 +735,41 @@ export interface InfiniteQueryObserverBaseResult<
712735
TData = unknown,
713736
TError = DefaultError,
714737
> extends QueryObserverBaseResult<TData, TError> {
738+
/**
739+
* This function allows you to fetch the next "page" of results.
740+
*/
715741
fetchNextPage: (
716742
options?: FetchNextPageOptions,
717743
) => Promise<InfiniteQueryObserverResult<TData, TError>>
744+
/**
745+
* This function allows you to fetch the previous "page" of results.
746+
*/
718747
fetchPreviousPage: (
719748
options?: FetchPreviousPageOptions,
720749
) => Promise<InfiniteQueryObserverResult<TData, TError>>
750+
/**
751+
* Will be `true` if there is a next page to be fetched (known via the `getNextPageParam` option).
752+
*/
721753
hasNextPage: boolean
754+
/**
755+
* Will be `true` if there is a previous page to be fetched (known via the `getPreviousPageParam` option).
756+
*/
722757
hasPreviousPage: boolean
758+
/**
759+
* Will be `true` if the query failed while fetching the next page.
760+
*/
761+
isFetchNextPageError: boolean
762+
/**
763+
* Will be `true` while fetching the next page with `fetchNextPage`.
764+
*/
723765
isFetchingNextPage: boolean
766+
/**
767+
* Will be `true` if the query failed while fetching the previous page.
768+
*/
769+
isFetchPreviousPageError: boolean
770+
/**
771+
* Will be `true` while fetching the previous page with `fetchPreviousPage`.
772+
*/
724773
isFetchingPreviousPage: boolean
725774
}
726775

@@ -734,6 +783,8 @@ export interface InfiniteQueryObserverPendingResult<
734783
isPending: true
735784
isLoadingError: false
736785
isRefetchError: false
786+
isFetchNextPageError: false
787+
isFetchPreviousPageError: false
737788
isSuccess: false
738789
status: 'pending'
739790
}
@@ -749,6 +800,8 @@ export interface InfiniteQueryObserverLoadingResult<
749800
isLoading: true
750801
isLoadingError: false
751802
isRefetchError: false
803+
isFetchNextPageError: false
804+
isFetchPreviousPageError: false
752805
isSuccess: false
753806
status: 'pending'
754807
}
@@ -764,6 +817,8 @@ export interface InfiniteQueryObserverLoadingErrorResult<
764817
isLoading: false
765818
isLoadingError: true
766819
isRefetchError: false
820+
isFetchNextPageError: false
821+
isFetchPreviousPageError: false
767822
isSuccess: false
768823
status: 'error'
769824
}
@@ -779,6 +834,8 @@ export interface InfiniteQueryObserverRefetchErrorResult<
779834
isLoading: false
780835
isLoadingError: false
781836
isRefetchError: true
837+
isFetchNextPageError: false
838+
isFetchPreviousPageError: false
782839
isSuccess: false
783840
status: 'error'
784841
}
@@ -794,6 +851,8 @@ export interface InfiniteQueryObserverSuccessResult<
794851
isLoading: false
795852
isLoadingError: false
796853
isRefetchError: false
854+
isFetchNextPageError: false
855+
isFetchPreviousPageError: false
797856
isSuccess: true
798857
status: 'success'
799858
}

0 commit comments

Comments
 (0)