diff --git a/src/core/queryObserver.ts b/src/core/queryObserver.ts index 99394e4a57..691cb5c00a 100644 --- a/src/core/queryObserver.ts +++ b/src/core/queryObserver.ts @@ -10,6 +10,7 @@ import { import { notifyManager } from './notifyManager' import type { PlaceholderDataFunction, + QueryObserverBaseResult, QueryObserverOptions, QueryObserverResult, QueryOptions, @@ -390,7 +391,7 @@ export class QueryObserver< } } - return { + const result: QueryObserverBaseResult = { ...getStatusProps(status), data, error: state.error, @@ -398,13 +399,17 @@ export class QueryObserver< isFetched: state.dataUpdateCount > 0, isFetchedAfterMount: state.dataUpdateCount > this.initialDataUpdateCount, isFetching, + isLoadingError: status === 'error' && state.dataUpdatedAt === 0, isPlaceholderData, isPreviousData, + isRefetchError: status === 'error' && state.dataUpdatedAt !== 0, isStale: this.isStale(), refetch: this.refetch, remove: this.remove, updatedAt, } + + return result as QueryObserverResult } private updateResult(willFetch?: boolean): void { diff --git a/src/core/types.ts b/src/core/types.ts index 779b9e4a55..d5747a6498 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -217,7 +217,7 @@ export interface FetchPreviousPageOptions extends ResultOptions { export type QueryStatus = 'idle' | 'loading' | 'error' | 'success' -export interface QueryObserverResult { +export interface QueryObserverBaseResult { data: TData | undefined error: TError | null failureCount: number @@ -227,8 +227,10 @@ export interface QueryObserverResult { isFetching: boolean isIdle: boolean isLoading: boolean - isPreviousData: boolean + isLoadingError: boolean isPlaceholderData: boolean + isPreviousData: boolean + isRefetchError: boolean isStale: boolean isSuccess: boolean refetch: ( @@ -239,8 +241,86 @@ export interface QueryObserverResult { updatedAt: number } -export interface InfiniteQueryObserverResult - extends QueryObserverResult, TError> { +export interface QueryObserverIdleResult + extends QueryObserverBaseResult { + data: undefined + error: null + isError: false + isIdle: true + isLoading: false + isLoadingError: false + isRefetchError: false + isSuccess: false + status: 'idle' +} + +export interface QueryObserverLoadingResult + extends QueryObserverBaseResult { + data: undefined + error: null + isError: false + isIdle: false + isLoading: true + isLoadingError: false + isRefetchError: false + isSuccess: false + status: 'loading' +} + +export interface QueryObserverLoadingErrorResult< + TData = unknown, + TError = unknown +> extends QueryObserverBaseResult { + data: undefined + error: TError + isError: true + isIdle: false + isLoading: false + isLoadingError: true + isRefetchError: false + isSuccess: false + status: 'error' +} + +export interface QueryObserverRefetchErrorResult< + TData = unknown, + TError = unknown +> extends QueryObserverBaseResult { + data: TData + error: TError + isError: true + isIdle: false + isLoading: false + isLoadingError: false + isRefetchError: true + isSuccess: false + status: 'error' +} + +export interface QueryObserverSuccessResult + extends QueryObserverBaseResult { + data: TData + error: null + isError: false + isIdle: false + isLoading: false + isLoadingError: false + isRefetchError: false + isSuccess: true + status: 'success' +} + +export type QueryObserverResult = + | QueryObserverIdleResult + | QueryObserverLoadingErrorResult + | QueryObserverLoadingResult + | QueryObserverRefetchErrorResult + | QueryObserverSuccessResult + +export interface InfiniteQueryObserverBaseResult< + TData = unknown, + TError = unknown +> extends QueryObserverBaseResult, TError> { fetchNextPage: ( options?: FetchNextPageOptions ) => Promise> @@ -253,6 +333,88 @@ export interface InfiniteQueryObserverResult isFetchingPreviousPage: boolean } +export interface InfiniteQueryObserverIdleResult< + TData = unknown, + TError = unknown +> extends InfiniteQueryObserverBaseResult { + data: undefined + error: null + isError: false + isIdle: true + isLoading: false + isLoadingError: false + isRefetchError: false + isSuccess: false + status: 'idle' +} + +export interface InfiniteQueryObserverLoadingResult< + TData = unknown, + TError = unknown +> extends InfiniteQueryObserverBaseResult { + data: undefined + error: null + isError: false + isIdle: false + isLoading: true + isLoadingError: false + isRefetchError: false + isSuccess: false + status: 'loading' +} + +export interface InfiniteQueryObserverLoadingErrorResult< + TData = unknown, + TError = unknown +> extends InfiniteQueryObserverBaseResult { + data: undefined + error: TError + isError: true + isIdle: false + isLoading: false + isLoadingError: true + isRefetchError: false + isSuccess: false + status: 'error' +} + +export interface InfiniteQueryObserverRefetchErrorResult< + TData = unknown, + TError = unknown +> extends InfiniteQueryObserverBaseResult { + data: InfiniteData + error: TError + isError: true + isIdle: false + isLoading: false + isLoadingError: false + isRefetchError: true + isSuccess: false + status: 'error' +} + +export interface InfiniteQueryObserverSuccessResult< + TData = unknown, + TError = unknown +> extends InfiniteQueryObserverBaseResult { + data: InfiniteData + error: null + isError: false + isIdle: false + isLoading: false + isLoadingError: false + isRefetchError: false + isSuccess: true + status: 'success' +} + +export type InfiniteQueryObserverResult = + | InfiniteQueryObserverIdleResult + | InfiniteQueryObserverLoadingErrorResult + | InfiniteQueryObserverLoadingResult + | InfiniteQueryObserverRefetchErrorResult + | InfiniteQueryObserverSuccessResult + export type MutationKey = string | unknown[] export type MutationStatus = 'idle' | 'loading' | 'success' | 'error' diff --git a/src/react/tests/useInfiniteQuery.test.tsx b/src/react/tests/useInfiniteQuery.test.tsx index 7807e9275f..0e53082803 100644 --- a/src/react/tests/useInfiniteQuery.test.tsx +++ b/src/react/tests/useInfiniteQuery.test.tsx @@ -87,8 +87,10 @@ describe('useInfiniteQuery', () => { isFetchingPreviousPage: false, isIdle: false, isLoading: true, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: false, refetch: expect.any(Function), @@ -113,8 +115,10 @@ describe('useInfiniteQuery', () => { isFetchingPreviousPage: false, isIdle: false, isLoading: false, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: true, refetch: expect.any(Function), diff --git a/src/react/tests/useQuery.test.tsx b/src/react/tests/useQuery.test.tsx index d8714402a4..c7e7251600 100644 --- a/src/react/tests/useQuery.test.tsx +++ b/src/react/tests/useQuery.test.tsx @@ -116,20 +116,36 @@ describe('useQuery', () => { const states: UseQueryResult[] = [] function Page() { - const state = useQuery(key, () => 'test') + const state = useQuery(key, () => 'test') states.push(state) - return ( -
-

Status: {state.status}

-
- ) + if (state.isIdle) { + expectType(state.data) + expectType(state.error) + return idle + } + + if (state.isLoading) { + expectType(state.data) + expectType(state.error) + return loading + } + + if (state.isLoadingError) { + expectType(state.data) + expectType(state.error) + return {state.error} + } + + expectType(state.data) + expectType(state.error) + return {state.data} } - const rendered = renderWithClient(queryClient, ) + renderWithClient(queryClient, ) - await waitFor(() => rendered.getByText('Status: success')) + await sleep(10) expect(states[0]).toEqual({ data: undefined, @@ -141,8 +157,10 @@ describe('useQuery', () => { isFetching: true, isIdle: false, isLoading: true, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: false, refetch: expect.any(Function), @@ -161,8 +179,10 @@ describe('useQuery', () => { isFetching: false, isIdle: false, isLoading: false, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: true, refetch: expect.any(Function), @@ -211,8 +231,10 @@ describe('useQuery', () => { isFetching: true, isIdle: false, isLoading: true, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: false, refetch: expect.any(Function), @@ -231,8 +253,10 @@ describe('useQuery', () => { isFetching: true, isIdle: false, isLoading: true, - isPreviousData: false, + isLoadingError: false, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: false, refetch: expect.any(Function), @@ -251,8 +275,10 @@ describe('useQuery', () => { isFetching: false, isIdle: false, isLoading: false, - isPreviousData: false, + isLoadingError: true, isPlaceholderData: false, + isPreviousData: false, + isRefetchError: false, isStale: true, isSuccess: false, refetch: expect.any(Function), diff --git a/src/react/types.ts b/src/react/types.ts index 4e6ab655b7..c44dec2f13 100644 --- a/src/react/types.ts +++ b/src/react/types.ts @@ -34,14 +34,20 @@ export interface UseInfiniteQueryOptions< TQueryData > {} -export interface UseBaseQueryResult - extends QueryObserverResult {} +export type UseBaseQueryResult< + TData = unknown, + TError = unknown +> = QueryObserverResult -export interface UseQueryResult - extends UseBaseQueryResult {} +export type UseQueryResult< + TData = unknown, + TError = unknown +> = UseBaseQueryResult -export interface UseInfiniteQueryResult - extends InfiniteQueryObserverResult {} +export type UseInfiniteQueryResult< + TData = unknown, + TError = unknown +> = InfiniteQueryObserverResult export interface UseMutationOptions< TData = unknown,