Skip to content

Commit 8f6bdf3

Browse files
committed
2 parents 365af29 + caa6522 commit 8f6bdf3

File tree

4 files changed

+54
-28
lines changed

4 files changed

+54
-28
lines changed

src/core/query.ts

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export class Query<TResult, TError> {
121121
private continueFetch?: () => void
122122
private isTransportCancelable?: boolean
123123
private notifyGlobalListeners: (query: Query<TResult, TError>) => void
124+
private enableTimeouts: boolean
124125

125126
constructor(init: QueryInitConfig<TResult, TError>) {
126127
this.config = init.config
@@ -130,16 +131,13 @@ export class Query<TResult, TError> {
130131
this.notifyGlobalListeners = init.notifyGlobalListeners
131132
this.observers = []
132133
this.state = getDefaultState(init.config)
134+
this.enableTimeouts = false
135+
}
133136

134-
// If the query started with data, schedule
135-
// a stale timeout
136-
if (!isServer && this.state.data) {
137-
this.scheduleStaleTimeout()
138-
139-
// Schedule for garbage collection in case
140-
// nothing subscribes to this query
141-
this.scheduleCacheTimeout()
142-
}
137+
activateTimeouts(): void {
138+
this.enableTimeouts = true
139+
this.rescheduleStaleTimeout()
140+
this.rescheduleGarbageCollection()
143141
}
144142

145143
updateConfig(config: QueryConfig<TResult, TError>): void {
@@ -152,20 +150,33 @@ export class Query<TResult, TError> {
152150
this.notifyGlobalListeners(this)
153151
}
154152

155-
private scheduleStaleTimeout(): void {
153+
private rescheduleStaleTimeout(): void {
156154
if (isServer) {
157155
return
158156
}
159157

160158
this.clearStaleTimeout()
161159

162-
if (this.state.isStale || this.config.staleTime === Infinity) {
160+
if (
161+
!this.enableTimeouts ||
162+
this.state.isStale ||
163+
this.state.status !== QueryStatus.Success ||
164+
this.config.staleTime === Infinity
165+
) {
163166
return
164167
}
165168

169+
const staleTime = this.config.staleTime || 0
170+
let timeout = staleTime
171+
if (this.state.updatedAt) {
172+
const timeElapsed = Date.now() - this.state.updatedAt
173+
const timeUntilStale = staleTime - timeElapsed
174+
timeout = Math.max(timeUntilStale, 0)
175+
}
176+
166177
this.staleTimeout = setTimeout(() => {
167178
this.invalidate()
168-
}, this.config.staleTime)
179+
}, timeout)
169180
}
170181

171182
invalidate(): void {
@@ -178,14 +189,18 @@ export class Query<TResult, TError> {
178189
this.dispatch({ type: ActionType.MarkStale })
179190
}
180191

181-
private scheduleCacheTimeout(): void {
192+
private rescheduleGarbageCollection(): void {
182193
if (isServer) {
183194
return
184195
}
185196

186197
this.clearCacheTimeout()
187198

188-
if (this.config.cacheTime === Infinity) {
199+
if (
200+
!this.enableTimeouts ||
201+
this.config.cacheTime === Infinity ||
202+
this.observers.length > 0
203+
) {
189204
return
190205
}
191206

@@ -262,10 +277,7 @@ export class Query<TResult, TError> {
262277
canFetchMore,
263278
})
264279

265-
if (!isStale) {
266-
// Schedule a fresh invalidation!
267-
this.scheduleStaleTimeout()
268-
}
280+
this.rescheduleStaleTimeout()
269281
}
270282

271283
clear(): void {
@@ -337,10 +349,9 @@ export class Query<TResult, TError> {
337349
if (this.isTransportCancelable) {
338350
this.cancel()
339351
}
340-
341-
// Schedule garbage collection
342-
this.scheduleCacheTimeout()
343352
}
353+
354+
this.rescheduleGarbageCollection()
344355
}
345356

346357
private async tryFetchData<T>(

src/core/queryCache.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,9 @@ export class QueryCache {
302302
// https://github.com/tannerlinsley/react-query/issues/652
303303
const configWithoutRetry = { retry: false, ...config }
304304

305+
let query
305306
try {
306-
const query = this.buildQuery<TResult, TError>(
307-
queryKey,
308-
configWithoutRetry
309-
)
307+
query = this.buildQuery<TResult, TError>(queryKey, configWithoutRetry)
310308
if (options?.force || query.state.isStale) {
311309
await query.fetch()
312310
}
@@ -316,6 +314,14 @@ export class QueryCache {
316314
throw error
317315
}
318316
return
317+
} finally {
318+
if (query) {
319+
// When prefetching, no observer is tied to the query,
320+
// so to avoid immediate garbage collection of the still
321+
// empty query, we wait with activating timeouts until
322+
// the prefetch is done
323+
query.activateTimeouts()
324+
}
319325
}
320326
}
321327

@@ -331,11 +337,13 @@ export class QueryCache {
331337
return
332338
}
333339

334-
this.buildQuery<TResult, TError>(queryKey, {
340+
const newQuery = this.buildQuery<TResult, TError>(queryKey, {
335341
initialStale: typeof config?.staleTime === 'undefined',
336342
initialData: functionalUpdate(updater, undefined),
337343
...config,
338344
})
345+
346+
newQuery.activateTimeouts()
339347
}
340348
}
341349

src/core/queryObserver.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ export class QueryObserver<TResult, TError> {
211211
return false
212212
}
213213

214+
newQuery.activateTimeouts()
215+
214216
this.previousResult = this.currentResult
215217
this.currentQuery = newQuery
216218
this.currentResult = this.createResult()

src/react/tests/useQuery.test.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -800,7 +800,10 @@ describe('useQuery', () => {
800800
queryFn.mockImplementation(() => 'data')
801801

802802
const prefetchQueryFn = jest.fn()
803-
prefetchQueryFn.mockImplementation(() => 'not yet...')
803+
prefetchQueryFn.mockImplementation(async () => {
804+
await sleep(10)
805+
return 'not yet...'
806+
})
804807

805808
await queryCache.prefetchQuery(key, prefetchQueryFn, {
806809
staleTime: 1000,
@@ -809,7 +812,9 @@ describe('useQuery', () => {
809812
await sleep(0)
810813

811814
function Page() {
812-
useQuery(key, queryFn)
815+
useQuery(key, queryFn, {
816+
staleTime: 1000,
817+
})
813818
return null
814819
}
815820

0 commit comments

Comments
 (0)