Skip to content

Commit bc4a001

Browse files
committed
fix: prevent infinite fetch loop
1 parent 8b57710 commit bc4a001

File tree

3 files changed

+60
-8
lines changed

3 files changed

+60
-8
lines changed

src/core/query.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,12 @@ export class Query<TData = unknown, TError = unknown, TQueryFnData = TData> {
163163
private scheduleGc(): void {
164164
this.clearGcTimeout()
165165

166-
if (!this.observers.length && isValidTimeout(this.cacheTime)) {
167-
if (!this.cacheTime) {
168-
this.cache.remove(this)
169-
} else {
170-
this.gcTimeout = setTimeout(() => {
166+
if (isValidTimeout(this.cacheTime)) {
167+
this.gcTimeout = setTimeout(() => {
168+
if (!this.observers.length) {
171169
this.cache.remove(this)
172-
}, this.cacheTime)
173-
}
170+
}
171+
}, this.cacheTime)
174172
}
175173
}
176174

@@ -311,7 +309,11 @@ export class Query<TData = unknown, TError = unknown, TQueryFnData = TData> {
311309
}
312310
}
313311

314-
this.scheduleGc()
312+
if (this.cacheTime) {
313+
this.scheduleGc()
314+
} else {
315+
this.cache.remove(this)
316+
}
315317
}
316318

317319
this.cache.notify(this)

src/core/queryObserver.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ export class QueryObserver<
7373

7474
protected onSubscribe(): void {
7575
if (this.listeners.length === 1) {
76+
this.updateQuery()
77+
7678
this.currentQuery.addObserver(this)
7779

7880
if (this.willFetchOnMount()) {

src/react/tests/useQuery.test.tsx

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,54 @@ describe('useQuery', () => {
463463
expect(states[1]).toMatchObject({ data: 'data' })
464464
})
465465

466+
it('should create a new query when re-mounting with cacheTime 0', async () => {
467+
const key = queryKey()
468+
const states: UseQueryResult<string>[] = []
469+
470+
function Page() {
471+
const [toggle, setToggle] = React.useState(false)
472+
473+
React.useEffect(() => {
474+
setActTimeout(() => {
475+
setToggle(true)
476+
}, 20)
477+
}, [setToggle])
478+
479+
return toggle ? <Component key="1" /> : <Component key="2" />
480+
}
481+
482+
function Component() {
483+
const state = useQuery(
484+
key,
485+
async () => {
486+
await sleep(5)
487+
return 'data'
488+
},
489+
{
490+
cacheTime: 0,
491+
}
492+
)
493+
states.push(state)
494+
return null
495+
}
496+
497+
renderWithClient(queryClient, <Page />)
498+
499+
await sleep(100)
500+
501+
expect(states.length).toBe(5)
502+
// First load
503+
expect(states[0]).toMatchObject({ isLoading: true, isSuccess: false })
504+
// First success
505+
expect(states[1]).toMatchObject({ isLoading: false, isSuccess: true })
506+
// Switch
507+
expect(states[2]).toMatchObject({ isLoading: false, isSuccess: true })
508+
// Second load
509+
expect(states[3]).toMatchObject({ isLoading: true, isSuccess: false })
510+
// Second success
511+
expect(states[4]).toMatchObject({ isLoading: false, isSuccess: true })
512+
})
513+
466514
it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => {
467515
const key = queryKey()
468516
const states: UseQueryResult<string>[] = []

0 commit comments

Comments
 (0)