Skip to content

Commit 27e72de

Browse files
authored
fix(useQuery): cleanup queries even if they have been fetching (TanStack#2950)
* fix(useQuery): cleanup queries even if they have been fetching * fix(useQuery): cleanup queries even if they have been fetching do not re-schedule garbage collection if a query is fetching and we never had any observers subscribed; this is necessary to make suspense work, because with suspense, we always throw before we subscribe, so the garbage collection would prematurely remove the query, resulting in an infinite loop * fix(useQuery): cleanup queries even if they have been fetching only schedule garbage collection when the last observer unsubscribes rather than in the constructor this works around the suspense issue because we only add observers after we've loaded as we throw before that.
1 parent ebd22b2 commit 27e72de

File tree

2 files changed

+29
-3
lines changed

2 files changed

+29
-3
lines changed

src/core/query.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ export class Query<
175175
this.initialState = config.state || this.getDefaultState(this.options)
176176
this.state = this.initialState
177177
this.meta = config.meta
178-
this.scheduleGc()
179178
}
180179

181180
private setOptions(
@@ -214,8 +213,12 @@ export class Query<
214213
}
215214

216215
private optionalRemove() {
217-
if (!this.observers.length && !this.state.isFetching) {
218-
this.cache.remove(this)
216+
if (!this.observers.length) {
217+
if (this.state.isFetching) {
218+
this.scheduleGc()
219+
} else {
220+
this.cache.remove(this)
221+
}
219222
}
220223
}
221224

src/core/tests/query.test.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -544,6 +544,29 @@ describe('query', () => {
544544
expect(queryCache.find(key)).toBeUndefined()
545545
})
546546

547+
test('should be garbage collected later when unsubscribed and query is fetching', async () => {
548+
const key = queryKey()
549+
const observer = new QueryObserver(queryClient, {
550+
queryKey: key,
551+
queryFn: async () => {
552+
await sleep(20)
553+
return 'data'
554+
},
555+
cacheTime: 10,
556+
})
557+
const unsubscribe = observer.subscribe()
558+
await sleep(20)
559+
expect(queryCache.find(key)).toBeDefined()
560+
observer.refetch()
561+
unsubscribe()
562+
await sleep(10)
563+
// unsubscribe should not remove even though cacheTime has elapsed b/c query is still fetching
564+
expect(queryCache.find(key)).toBeDefined()
565+
await sleep(10)
566+
// should be removed after an additional staleTime wait
567+
expect(queryCache.find(key)).toBeUndefined()
568+
})
569+
547570
test('should not be garbage collected unless there are no subscribers', async () => {
548571
const key = queryKey()
549572
const observer = new QueryObserver(queryClient, {

0 commit comments

Comments
 (0)