Skip to content

Commit

Permalink
feat: add isPreviousData and isFetchedAfterMount flags (#961)
Browse files Browse the repository at this point in the history
  • Loading branch information
boschni authored Sep 3, 2020
1 parent 7c22791 commit c6c0de6
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 43 deletions.
10 changes: 8 additions & 2 deletions docs/src/pages/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ const {
error,
failureCount,
isError,
isFetchedAfterMount,
isFetching,
isIdle,
isLoading,
isPreviousData,
isStale,
isSuccess,
refetch,
Expand Down Expand Up @@ -100,8 +102,7 @@ const queryInfo = useQuery({
- Set this to `true` or `false` to enable/disable automatic refetching on reconnect for this query.
- `notifyOnStatusChange: Boolean`
- Optional
- Whether a change to the query status should re-render a component.
- If set to `false`, the component will only re-render when the actual `data` or `error` changes.
- Set this to `false` to only re-render when there are changes to `data` or `error`.
- Defaults to `true`.
- `onSuccess: Function(data) => data`
- Optional
Expand Down Expand Up @@ -170,6 +171,11 @@ const queryInfo = useQuery({
- The error object for the query, if an error was thrown.
- `isStale: Boolean`
- Will be `true` if the cache data is stale.
- `isPreviousData: Boolean`
- Will be `true` when `keepPreviousData` is set and data from the previous query is returned.
- `isFetchedAfterMount: Boolean`
- Will be `true` if the query has been fetched after the component mounted.
- This property can be used to not show any previously cached data.
- `isFetching: Boolean`
- Defaults to `true` so long as `manual` is set to `false`
- Will be `true` if the query is currently fetching, including background fetching.
Expand Down
12 changes: 6 additions & 6 deletions src/core/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,16 @@ export const DEFAULT_STALE_TIME = 0
export const DEFAULT_CACHE_TIME = 5 * 60 * 1000
export const DEFAULT_CONFIG: ReactQueryConfig = {
queries: {
queryKeySerializerFn: defaultQueryKeySerializerFn,
cacheTime: DEFAULT_CACHE_TIME,
enabled: true,
notifyOnStatusChange: true,
queryKeySerializerFn: defaultQueryKeySerializerFn,
refetchOnMount: true,
refetchOnReconnect: true,
refetchOnWindowFocus: true,
retry: 3,
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
staleTime: DEFAULT_STALE_TIME,
cacheTime: DEFAULT_CACHE_TIME,
refetchOnWindowFocus: true,
refetchOnReconnect: true,
refetchOnMount: true,
notifyOnStatusChange: true,
structuralSharing: true,
},
}
Expand Down
21 changes: 9 additions & 12 deletions src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
isDocumentVisible,
isOnline,
isServer,
noop,
replaceEqualDeep,
sleep,
} from './utils'
Expand Down Expand Up @@ -38,6 +39,7 @@ export interface QueryState<TResult, TError> {
data?: TResult
error: TError | null
failureCount: number
fetchedCount: number
isError: boolean
isFetched: boolean
isFetching: boolean
Expand Down Expand Up @@ -229,7 +231,7 @@ export class Query<TResult, TError> {
)
}

async onWindowFocus(): Promise<void> {
onWindowFocus(): void {
if (
this.observers.some(
observer =>
Expand All @@ -238,17 +240,13 @@ export class Query<TResult, TError> {
observer.config.refetchOnWindowFocus
)
) {
try {
await this.fetch()
} catch {
// ignore
}
this.fetch().catch(noop)
}

this.continue()
}

async onOnline(): Promise<void> {
onOnline(): void {
if (
this.observers.some(
observer =>
Expand All @@ -257,11 +255,7 @@ export class Query<TResult, TError> {
observer.config.refetchOnReconnect
)
) {
try {
await this.fetch()
} catch {
// ignore
}
this.fetch().catch(noop)
}

this.continue()
Expand Down Expand Up @@ -612,6 +606,7 @@ function getDefaultState<TResult, TError>(
isFetching: initialStatus === QueryStatus.Loading,
isFetchingMore: false,
failureCount: 0,
fetchedCount: 0,
data: initialData,
updatedAt: Date.now(),
canFetchMore: hasMorePages(config, initialData),
Expand Down Expand Up @@ -646,6 +641,7 @@ export function queryReducer<TResult, TError>(
...getStatusProps(QueryStatus.Success),
data: action.data,
error: null,
fetchedCount: state.fetchedCount + 1,
isFetched: true,
isFetching: false,
isFetchingMore: false,
Expand All @@ -658,6 +654,7 @@ export function queryReducer<TResult, TError>(
...state,
...getStatusProps(QueryStatus.Error),
error: action.error,
fetchedCount: state.fetchedCount + 1,
isFetched: true,
isFetching: false,
isFetchingMore: false,
Expand Down
14 changes: 12 additions & 2 deletions src/core/queryObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ export class QueryObserver<TResult, TError> {
private currentResult!: QueryResult<TResult, TError>
private previousQueryResult?: QueryResult<TResult, TError>
private updateListener?: UpdateListener<TResult, TError>
private initialFetchedCount: number
private staleTimeoutId?: number
private refetchIntervalId?: number
private started?: boolean

constructor(config: QueryObserverConfig<TResult, TError>) {
this.config = config
this.queryCache = config.queryCache!
this.initialFetchedCount = 0

// Bind exposed methods
this.clear = this.clear.bind(this)
Expand Down Expand Up @@ -100,6 +102,10 @@ export class QueryObserver<TResult, TError> {
return this.currentResult.isStale
}

getCurrentQuery(): Query<TResult, TError> {
return this.currentQuery
}

getCurrentResult(): QueryResult<TResult, TError> {
return this.currentResult
}
Expand Down Expand Up @@ -224,16 +230,18 @@ export class QueryObserver<TResult, TError> {
const { currentQuery, currentResult, previousQueryResult, config } = this
const { state } = currentQuery
let { data, status, updatedAt } = state
let isPreviousData = false

// Keep previous data if needed
if (
config.keepPreviousData &&
state.isLoading &&
(state.isIdle || state.isLoading) &&
previousQueryResult?.isSuccess
) {
data = previousQueryResult.data
updatedAt = previousQueryResult.updatedAt
status = previousQueryResult.status
isPreviousData = true
}

let isStale = false
Expand Down Expand Up @@ -261,10 +269,11 @@ export class QueryObserver<TResult, TError> {
failureCount: state.failureCount,
fetchMore: this.fetchMore,
isFetched: state.isFetched,
isFetchedAfterMount: state.fetchedCount > this.initialFetchedCount,
isFetching: state.isFetching,
isFetchingMore: state.isFetchingMore,
isPreviousData,
isStale,
query: currentQuery,
refetch: this.refetch,
updatedAt,
}
Expand All @@ -288,6 +297,7 @@ export class QueryObserver<TResult, TError> {

this.previousQueryResult = this.currentResult
this.currentQuery = newQuery
this.initialFetchedCount = newQuery.state.fetchedCount
this.updateResult()

if (this.started) {
Expand Down
10 changes: 3 additions & 7 deletions src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Query, FetchMoreOptions, RefetchOptions } from './query'
import type { FetchMoreOptions, RefetchOptions } from './query'
import type { QueryCache } from './queryCache'

export type QueryKey =
Expand Down Expand Up @@ -34,11 +34,6 @@ export type QueryKeySerializerFunction = (
) => [string, QueryKey[]]

export interface BaseQueryConfig<TResult, TError = unknown, TData = TResult> {
/**
* Set this to `false` to disable automatic refetching when the query mounts or changes query keys.
* To refetch the query, use the `refetch` method returned from the `useQuery` instance.
*/
enabled?: boolean | unknown
/**
* If `false`, failed queries will not retry by default.
* If `true`, failed queries will retry infinitely., failureCount: num
Expand Down Expand Up @@ -180,13 +175,14 @@ export interface QueryResultBase<TResult, TError = unknown> {
) => Promise<TResult | undefined>
isError: boolean
isFetched: boolean
isFetchedAfterMount: boolean
isFetching: boolean
isFetchingMore?: IsFetchingMoreValue
isIdle: boolean
isLoading: boolean
isStale: boolean
isSuccess: boolean
query: Query<TResult, TError>
isPreviousData: boolean
refetch: (options?: RefetchOptions) => Promise<TResult | undefined>
status: QueryStatus
updatedAt: number
Expand Down
2 changes: 1 addition & 1 deletion src/core/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const uid = () => _uid++

export const isServer = typeof window === 'undefined'

function noop(): void {
export function noop(): void {
return void 0
}

Expand Down
12 changes: 10 additions & 2 deletions src/react/tests/useInfiniteQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,13 +68,14 @@ describe('useInfiniteQuery', () => {
fetchMore: expect.any(Function),
isError: false,
isFetched: false,
isFetchedAfterMount: false,
isFetching: true,
isFetchingMore: false,
isIdle: false,
isLoading: true,
isPreviousData: false,
isStale: true,
isSuccess: false,
query: expect.any(Object),
refetch: expect.any(Function),
status: 'loading',
updatedAt: expect.any(Number),
Expand All @@ -96,12 +97,13 @@ describe('useInfiniteQuery', () => {
isFetchingMore: false,
isError: false,
isFetched: true,
isFetchedAfterMount: true,
isFetching: false,
isIdle: false,
isLoading: false,
isPreviousData: false,
isStale: true,
isSuccess: true,
query: expect.any(Object),
refetch: expect.any(Function),
status: 'success',
updatedAt: expect.any(Number),
Expand Down Expand Up @@ -152,36 +154,42 @@ describe('useInfiniteQuery', () => {
isFetching: true,
isFetchingMore: false,
isSuccess: false,
isPreviousData: false,
})
expect(states[1]).toMatchObject({
data: ['0-desc'],
isFetching: false,
isFetchingMore: false,
isSuccess: true,
isPreviousData: false,
})
expect(states[2]).toMatchObject({
data: ['0-desc'],
isFetching: true,
isFetchingMore: 'next',
isSuccess: true,
isPreviousData: false,
})
expect(states[3]).toMatchObject({
data: ['0-desc', '1-desc'],
isFetching: false,
isFetchingMore: false,
isSuccess: true,
isPreviousData: false,
})
expect(states[4]).toMatchObject({
data: ['0-desc', '1-desc'],
isFetching: true,
isFetchingMore: false,
isSuccess: true,
isPreviousData: true,
})
expect(states[5]).toMatchObject({
data: ['0-asc'],
isFetching: false,
isFetchingMore: false,
isSuccess: true,
isPreviousData: false,
})
})

Expand Down
7 changes: 5 additions & 2 deletions src/react/tests/usePaginatedQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ describe('usePaginatedQuery', () => {
fetchMore: expect.any(Function),
isError: false,
isFetched: false,
isFetchedAfterMount: false,
isFetching: true,
isFetchingMore: false,
isIdle: false,
isLoading: true,
isPreviousData: false,
isStale: true,
isSuccess: false,
query: expect.any(Object),
latestData: undefined,
resolvedData: undefined,
refetch: expect.any(Function),
Expand All @@ -64,13 +65,14 @@ describe('usePaginatedQuery', () => {
fetchMore: expect.any(Function),
isError: false,
isFetched: true,
isFetchedAfterMount: true,
isFetching: false,
isFetchingMore: false,
isIdle: false,
isLoading: false,
isPreviousData: false,
isStale: true,
isSuccess: true,
query: expect.any(Object),
latestData: 1,
resolvedData: 1,
refetch: expect.any(Function),
Expand Down Expand Up @@ -208,6 +210,7 @@ describe('usePaginatedQuery', () => {
},
{
enabled: searchTerm,
keepPreviousData: page !== 1,
}
)

Expand Down
Loading

0 comments on commit c6c0de6

Please sign in to comment.