Skip to content

Commit c6c0de6

Browse files
authored
feat: add isPreviousData and isFetchedAfterMount flags (#961)
1 parent 7c22791 commit c6c0de6

File tree

11 files changed

+189
-43
lines changed

11 files changed

+189
-43
lines changed

docs/src/pages/docs/api.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@ const {
1212
error,
1313
failureCount,
1414
isError,
15+
isFetchedAfterMount,
1516
isFetching,
1617
isIdle,
1718
isLoading,
19+
isPreviousData,
1820
isStale,
1921
isSuccess,
2022
refetch,
@@ -100,8 +102,7 @@ const queryInfo = useQuery({
100102
- Set this to `true` or `false` to enable/disable automatic refetching on reconnect for this query.
101103
- `notifyOnStatusChange: Boolean`
102104
- Optional
103-
- Whether a change to the query status should re-render a component.
104-
- If set to `false`, the component will only re-render when the actual `data` or `error` changes.
105+
- Set this to `false` to only re-render when there are changes to `data` or `error`.
105106
- Defaults to `true`.
106107
- `onSuccess: Function(data) => data`
107108
- Optional
@@ -170,6 +171,11 @@ const queryInfo = useQuery({
170171
- The error object for the query, if an error was thrown.
171172
- `isStale: Boolean`
172173
- Will be `true` if the cache data is stale.
174+
- `isPreviousData: Boolean`
175+
- Will be `true` when `keepPreviousData` is set and data from the previous query is returned.
176+
- `isFetchedAfterMount: Boolean`
177+
- Will be `true` if the query has been fetched after the component mounted.
178+
- This property can be used to not show any previously cached data.
173179
- `isFetching: Boolean`
174180
- Defaults to `true` so long as `manual` is set to `false`
175181
- Will be `true` if the query is currently fetching, including background fetching.

src/core/config.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,16 @@ export const DEFAULT_STALE_TIME = 0
5151
export const DEFAULT_CACHE_TIME = 5 * 60 * 1000
5252
export const DEFAULT_CONFIG: ReactQueryConfig = {
5353
queries: {
54-
queryKeySerializerFn: defaultQueryKeySerializerFn,
54+
cacheTime: DEFAULT_CACHE_TIME,
5555
enabled: true,
56+
notifyOnStatusChange: true,
57+
queryKeySerializerFn: defaultQueryKeySerializerFn,
58+
refetchOnMount: true,
59+
refetchOnReconnect: true,
60+
refetchOnWindowFocus: true,
5661
retry: 3,
5762
retryDelay: attemptIndex => Math.min(1000 * 2 ** attemptIndex, 30000),
5863
staleTime: DEFAULT_STALE_TIME,
59-
cacheTime: DEFAULT_CACHE_TIME,
60-
refetchOnWindowFocus: true,
61-
refetchOnReconnect: true,
62-
refetchOnMount: true,
63-
notifyOnStatusChange: true,
6464
structuralSharing: true,
6565
},
6666
}

src/core/query.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
isDocumentVisible,
1010
isOnline,
1111
isServer,
12+
noop,
1213
replaceEqualDeep,
1314
sleep,
1415
} from './utils'
@@ -38,6 +39,7 @@ export interface QueryState<TResult, TError> {
3839
data?: TResult
3940
error: TError | null
4041
failureCount: number
42+
fetchedCount: number
4143
isError: boolean
4244
isFetched: boolean
4345
isFetching: boolean
@@ -229,7 +231,7 @@ export class Query<TResult, TError> {
229231
)
230232
}
231233

232-
async onWindowFocus(): Promise<void> {
234+
onWindowFocus(): void {
233235
if (
234236
this.observers.some(
235237
observer =>
@@ -238,17 +240,13 @@ export class Query<TResult, TError> {
238240
observer.config.refetchOnWindowFocus
239241
)
240242
) {
241-
try {
242-
await this.fetch()
243-
} catch {
244-
// ignore
245-
}
243+
this.fetch().catch(noop)
246244
}
247245

248246
this.continue()
249247
}
250248

251-
async onOnline(): Promise<void> {
249+
onOnline(): void {
252250
if (
253251
this.observers.some(
254252
observer =>
@@ -257,11 +255,7 @@ export class Query<TResult, TError> {
257255
observer.config.refetchOnReconnect
258256
)
259257
) {
260-
try {
261-
await this.fetch()
262-
} catch {
263-
// ignore
264-
}
258+
this.fetch().catch(noop)
265259
}
266260

267261
this.continue()
@@ -612,6 +606,7 @@ function getDefaultState<TResult, TError>(
612606
isFetching: initialStatus === QueryStatus.Loading,
613607
isFetchingMore: false,
614608
failureCount: 0,
609+
fetchedCount: 0,
615610
data: initialData,
616611
updatedAt: Date.now(),
617612
canFetchMore: hasMorePages(config, initialData),
@@ -646,6 +641,7 @@ export function queryReducer<TResult, TError>(
646641
...getStatusProps(QueryStatus.Success),
647642
data: action.data,
648643
error: null,
644+
fetchedCount: state.fetchedCount + 1,
649645
isFetched: true,
650646
isFetching: false,
651647
isFetchingMore: false,
@@ -658,6 +654,7 @@ export function queryReducer<TResult, TError>(
658654
...state,
659655
...getStatusProps(QueryStatus.Error),
660656
error: action.error,
657+
fetchedCount: state.fetchedCount + 1,
661658
isFetched: true,
662659
isFetching: false,
663660
isFetchingMore: false,

src/core/queryObserver.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ export class QueryObserver<TResult, TError> {
1515
private currentResult!: QueryResult<TResult, TError>
1616
private previousQueryResult?: QueryResult<TResult, TError>
1717
private updateListener?: UpdateListener<TResult, TError>
18+
private initialFetchedCount: number
1819
private staleTimeoutId?: number
1920
private refetchIntervalId?: number
2021
private started?: boolean
2122

2223
constructor(config: QueryObserverConfig<TResult, TError>) {
2324
this.config = config
2425
this.queryCache = config.queryCache!
26+
this.initialFetchedCount = 0
2527

2628
// Bind exposed methods
2729
this.clear = this.clear.bind(this)
@@ -100,6 +102,10 @@ export class QueryObserver<TResult, TError> {
100102
return this.currentResult.isStale
101103
}
102104

105+
getCurrentQuery(): Query<TResult, TError> {
106+
return this.currentQuery
107+
}
108+
103109
getCurrentResult(): QueryResult<TResult, TError> {
104110
return this.currentResult
105111
}
@@ -224,16 +230,18 @@ export class QueryObserver<TResult, TError> {
224230
const { currentQuery, currentResult, previousQueryResult, config } = this
225231
const { state } = currentQuery
226232
let { data, status, updatedAt } = state
233+
let isPreviousData = false
227234

228235
// Keep previous data if needed
229236
if (
230237
config.keepPreviousData &&
231-
state.isLoading &&
238+
(state.isIdle || state.isLoading) &&
232239
previousQueryResult?.isSuccess
233240
) {
234241
data = previousQueryResult.data
235242
updatedAt = previousQueryResult.updatedAt
236243
status = previousQueryResult.status
244+
isPreviousData = true
237245
}
238246

239247
let isStale = false
@@ -261,10 +269,11 @@ export class QueryObserver<TResult, TError> {
261269
failureCount: state.failureCount,
262270
fetchMore: this.fetchMore,
263271
isFetched: state.isFetched,
272+
isFetchedAfterMount: state.fetchedCount > this.initialFetchedCount,
264273
isFetching: state.isFetching,
265274
isFetchingMore: state.isFetchingMore,
275+
isPreviousData,
266276
isStale,
267-
query: currentQuery,
268277
refetch: this.refetch,
269278
updatedAt,
270279
}
@@ -288,6 +297,7 @@ export class QueryObserver<TResult, TError> {
288297

289298
this.previousQueryResult = this.currentResult
290299
this.currentQuery = newQuery
300+
this.initialFetchedCount = newQuery.state.fetchedCount
291301
this.updateResult()
292302

293303
if (this.started) {

src/core/types.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Query, FetchMoreOptions, RefetchOptions } from './query'
1+
import type { FetchMoreOptions, RefetchOptions } from './query'
22
import type { QueryCache } from './queryCache'
33

44
export type QueryKey =
@@ -34,11 +34,6 @@ export type QueryKeySerializerFunction = (
3434
) => [string, QueryKey[]]
3535

3636
export interface BaseQueryConfig<TResult, TError = unknown, TData = TResult> {
37-
/**
38-
* Set this to `false` to disable automatic refetching when the query mounts or changes query keys.
39-
* To refetch the query, use the `refetch` method returned from the `useQuery` instance.
40-
*/
41-
enabled?: boolean | unknown
4237
/**
4338
* If `false`, failed queries will not retry by default.
4439
* If `true`, failed queries will retry infinitely., failureCount: num
@@ -180,13 +175,14 @@ export interface QueryResultBase<TResult, TError = unknown> {
180175
) => Promise<TResult | undefined>
181176
isError: boolean
182177
isFetched: boolean
178+
isFetchedAfterMount: boolean
183179
isFetching: boolean
184180
isFetchingMore?: IsFetchingMoreValue
185181
isIdle: boolean
186182
isLoading: boolean
187183
isStale: boolean
188184
isSuccess: boolean
189-
query: Query<TResult, TError>
185+
isPreviousData: boolean
190186
refetch: (options?: RefetchOptions) => Promise<TResult | undefined>
191187
status: QueryStatus
192188
updatedAt: number

src/core/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const uid = () => _uid++
2929

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

32-
function noop(): void {
32+
export function noop(): void {
3333
return void 0
3434
}
3535

src/react/tests/useInfiniteQuery.test.tsx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,14 @@ describe('useInfiniteQuery', () => {
6868
fetchMore: expect.any(Function),
6969
isError: false,
7070
isFetched: false,
71+
isFetchedAfterMount: false,
7172
isFetching: true,
7273
isFetchingMore: false,
7374
isIdle: false,
7475
isLoading: true,
76+
isPreviousData: false,
7577
isStale: true,
7678
isSuccess: false,
77-
query: expect.any(Object),
7879
refetch: expect.any(Function),
7980
status: 'loading',
8081
updatedAt: expect.any(Number),
@@ -96,12 +97,13 @@ describe('useInfiniteQuery', () => {
9697
isFetchingMore: false,
9798
isError: false,
9899
isFetched: true,
100+
isFetchedAfterMount: true,
99101
isFetching: false,
100102
isIdle: false,
101103
isLoading: false,
104+
isPreviousData: false,
102105
isStale: true,
103106
isSuccess: true,
104-
query: expect.any(Object),
105107
refetch: expect.any(Function),
106108
status: 'success',
107109
updatedAt: expect.any(Number),
@@ -152,36 +154,42 @@ describe('useInfiniteQuery', () => {
152154
isFetching: true,
153155
isFetchingMore: false,
154156
isSuccess: false,
157+
isPreviousData: false,
155158
})
156159
expect(states[1]).toMatchObject({
157160
data: ['0-desc'],
158161
isFetching: false,
159162
isFetchingMore: false,
160163
isSuccess: true,
164+
isPreviousData: false,
161165
})
162166
expect(states[2]).toMatchObject({
163167
data: ['0-desc'],
164168
isFetching: true,
165169
isFetchingMore: 'next',
166170
isSuccess: true,
171+
isPreviousData: false,
167172
})
168173
expect(states[3]).toMatchObject({
169174
data: ['0-desc', '1-desc'],
170175
isFetching: false,
171176
isFetchingMore: false,
172177
isSuccess: true,
178+
isPreviousData: false,
173179
})
174180
expect(states[4]).toMatchObject({
175181
data: ['0-desc', '1-desc'],
176182
isFetching: true,
177183
isFetchingMore: false,
178184
isSuccess: true,
185+
isPreviousData: true,
179186
})
180187
expect(states[5]).toMatchObject({
181188
data: ['0-asc'],
182189
isFetching: false,
183190
isFetchingMore: false,
184191
isSuccess: true,
192+
isPreviousData: false,
185193
})
186194
})
187195

src/react/tests/usePaginatedQuery.test.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,14 @@ describe('usePaginatedQuery', () => {
4141
fetchMore: expect.any(Function),
4242
isError: false,
4343
isFetched: false,
44+
isFetchedAfterMount: false,
4445
isFetching: true,
4546
isFetchingMore: false,
4647
isIdle: false,
4748
isLoading: true,
49+
isPreviousData: false,
4850
isStale: true,
4951
isSuccess: false,
50-
query: expect.any(Object),
5152
latestData: undefined,
5253
resolvedData: undefined,
5354
refetch: expect.any(Function),
@@ -64,13 +65,14 @@ describe('usePaginatedQuery', () => {
6465
fetchMore: expect.any(Function),
6566
isError: false,
6667
isFetched: true,
68+
isFetchedAfterMount: true,
6769
isFetching: false,
6870
isFetchingMore: false,
6971
isIdle: false,
7072
isLoading: false,
73+
isPreviousData: false,
7174
isStale: true,
7275
isSuccess: true,
73-
query: expect.any(Object),
7476
latestData: 1,
7577
resolvedData: 1,
7678
refetch: expect.any(Function),
@@ -208,6 +210,7 @@ describe('usePaginatedQuery', () => {
208210
},
209211
{
210212
enabled: searchTerm,
213+
keepPreviousData: page !== 1,
211214
}
212215
)
213216

0 commit comments

Comments
 (0)