Skip to content

Commit c0fc916

Browse files
authored
fix(core): do not refetch disabled queries (#3223)
* fix(core): do not refetch disabled queries with refetchQueries or invalidateQueries + refetchType "inactive" disabled queries (=queries that have observers which are all enabled:false) are matched as "inactive"; this is okay when searching for them via findAll or for removeQueries, but the docs clearly state that refetchQueries / invalidateQueries do not refetch disabled queries, and that the only way to refetch them is via refetch returned from useQuery; this is important when using enabled to signal that some dependencies are not yet ready some tests needed to be adapted because we used disabled observer + refetchQueries a lot. The easiest way to emulate the observers we wanted here was mostly with initialData + staleTime, and to get a real inactive query, we just need to subscribe + unsubscribe immediately * fix(core): do not refetch disabled queries add tests for refetchQueries + disabled * fix(core): do not refetch disabled queries update test to make more sense - title said disabled queries, but we had no disabled query; test now does the opposite of what it did before, but that's what this PR does :)
1 parent d4b6afc commit c0fc916

File tree

5 files changed

+83
-57
lines changed

5 files changed

+83
-57
lines changed

src/core/query.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ export class Query<
250250
return this.observers.some(observer => observer.options.enabled !== false)
251251
}
252252

253+
isDisabled(): boolean {
254+
return this.getObserversCount() > 0 && !this.isActive()
255+
}
256+
253257
isStale(): boolean {
254258
return (
255259
this.state.isInvalidated ||

src/core/queryClient.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -283,13 +283,16 @@ export class QueryClient {
283283
const [filters, options] = parseFilterArgs(arg1, arg2, arg3)
284284

285285
const promises = notifyManager.batch(() =>
286-
this.queryCache.findAll(filters).map(query =>
287-
query.fetch(undefined, {
288-
...options,
289-
cancelRefetch: options?.cancelRefetch ?? true,
290-
meta: { refetchPage: filters?.refetchPage },
291-
})
292-
)
286+
this.queryCache
287+
.findAll(filters)
288+
.filter(query => !query.isDisabled())
289+
.map(query =>
290+
query.fetch(undefined, {
291+
...options,
292+
cancelRefetch: options?.cancelRefetch ?? true,
293+
meta: { refetchPage: filters?.refetchPage },
294+
})
295+
)
293296
)
294297

295298
let promise = Promise.all(promises).then(noop)

src/core/tests/queryClient.test.tsx

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,41 @@ describe('queryClient', () => {
707707
})
708708

709709
describe('refetchQueries', () => {
710+
test('should not refetch if all observers are disabled', async () => {
711+
const key = queryKey()
712+
const queryFn = jest.fn()
713+
await queryClient.fetchQuery(key, queryFn)
714+
const observer1 = new QueryObserver(queryClient, {
715+
queryKey: key,
716+
queryFn,
717+
enabled: false,
718+
})
719+
observer1.subscribe(() => undefined)
720+
await queryClient.refetchQueries()
721+
observer1.destroy()
722+
expect(queryFn).toHaveBeenCalledTimes(1)
723+
})
724+
test('should refetch if at least one observer is enabled', async () => {
725+
const key = queryKey()
726+
const queryFn = jest.fn()
727+
await queryClient.fetchQuery(key, queryFn)
728+
const observer1 = new QueryObserver(queryClient, {
729+
queryKey: key,
730+
queryFn,
731+
enabled: false,
732+
})
733+
const observer2 = new QueryObserver(queryClient, {
734+
queryKey: key,
735+
queryFn,
736+
refetchOnMount: false,
737+
})
738+
observer1.subscribe(() => undefined)
739+
observer2.subscribe(() => undefined)
740+
await queryClient.refetchQueries()
741+
observer1.destroy()
742+
observer2.destroy()
743+
expect(queryFn).toHaveBeenCalledTimes(2)
744+
})
710745
test('should refetch all queries when no arguments are given', async () => {
711746
const key1 = queryKey()
712747
const key2 = queryKey()
@@ -716,11 +751,13 @@ describe('queryClient', () => {
716751
await queryClient.fetchQuery(key2, queryFn2)
717752
const observer1 = new QueryObserver(queryClient, {
718753
queryKey: key1,
719-
enabled: false,
754+
staleTime: Infinity,
755+
initialData: 'initial',
720756
})
721757
const observer2 = new QueryObserver(queryClient, {
722758
queryKey: key1,
723-
enabled: false,
759+
staleTime: Infinity,
760+
initialData: 'initial',
724761
})
725762
observer1.subscribe(() => undefined)
726763
observer2.subscribe(() => undefined)
@@ -964,13 +1001,14 @@ describe('queryClient', () => {
9641001
queryKey: key1,
9651002
queryFn: queryFn1,
9661003
staleTime: Infinity,
967-
enabled: false,
1004+
refetchOnMount: false,
9681005
})
9691006
const unsubscribe = observer.subscribe(() => undefined)
970-
queryClient.invalidateQueries(key1, {
1007+
unsubscribe()
1008+
1009+
await queryClient.invalidateQueries(key1, {
9711010
refetchType: 'inactive',
9721011
})
973-
unsubscribe()
9741012
expect(queryFn1).toHaveBeenCalledTimes(2)
9751013
expect(queryFn2).toHaveBeenCalledTimes(1)
9761014
})
@@ -1002,23 +1040,19 @@ describe('queryClient', () => {
10021040
let fetchCount = 0
10031041
const observer = new QueryObserver(queryClient, {
10041042
queryKey: key,
1005-
enabled: false,
1043+
queryFn: ({ signal }) => {
1044+
return new Promise(resolve => {
1045+
fetchCount++
1046+
setTimeout(() => resolve(5), 10)
1047+
if (signal) {
1048+
signal.addEventListener('abort', abortFn)
1049+
}
1050+
})
1051+
},
10061052
initialData: 1,
10071053
})
10081054
observer.subscribe(() => undefined)
10091055

1010-
queryClient.fetchQuery(key, ({ signal }) => {
1011-
const promise = new Promise(resolve => {
1012-
fetchCount++
1013-
setTimeout(() => resolve(5), 10)
1014-
if (signal) {
1015-
signal.addEventListener('abort', abortFn)
1016-
}
1017-
})
1018-
1019-
return promise
1020-
})
1021-
10221056
await queryClient.refetchQueries()
10231057
observer.destroy()
10241058
if (typeof AbortSignal === 'function') {
@@ -1033,23 +1067,19 @@ describe('queryClient', () => {
10331067
let fetchCount = 0
10341068
const observer = new QueryObserver(queryClient, {
10351069
queryKey: key,
1036-
enabled: false,
1070+
queryFn: ({ signal }) => {
1071+
return new Promise(resolve => {
1072+
fetchCount++
1073+
setTimeout(() => resolve(5), 10)
1074+
if (signal) {
1075+
signal.addEventListener('abort', abortFn)
1076+
}
1077+
})
1078+
},
10371079
initialData: 1,
10381080
})
10391081
observer.subscribe(() => undefined)
10401082

1041-
queryClient.fetchQuery(key, ({ signal }) => {
1042-
const promise = new Promise(resolve => {
1043-
fetchCount++
1044-
setTimeout(() => resolve(5), 10)
1045-
if (signal) {
1046-
signal.addEventListener('abort', abortFn)
1047-
}
1048-
})
1049-
1050-
return promise
1051-
})
1052-
10531083
await queryClient.refetchQueries(undefined, { cancelRefetch: false })
10541084
observer.destroy()
10551085
if (typeof AbortSignal === 'function') {

src/devtools/devtools.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -703,8 +703,6 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
703703
}}
704704
>
705705
{queries.map((query, i) => {
706-
const isDisabled =
707-
query.getObserversCount() > 0 && !query.isActive()
708706
return (
709707
<div
710708
key={query.queryHash || i}
@@ -747,7 +745,7 @@ export const ReactQueryDevtoolsPanel = React.forwardRef<
747745
>
748746
{query.getObserversCount()}
749747
</div>
750-
{isDisabled ? (
748+
{query.isDisabled() ? (
751749
<div
752750
style={{
753751
flex: '0 0 auto',

src/reactjs/tests/useQuery.test.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,7 +1180,10 @@ describe('useQuery', () => {
11801180
await sleep(1)
11811181
return 'fetched'
11821182
},
1183-
{ enabled: false }
1183+
{
1184+
initialData: 'initial',
1185+
staleTime: Infinity,
1186+
}
11841187
)
11851188

11861189
results.push(result)
@@ -1266,7 +1269,7 @@ describe('useQuery', () => {
12661269
})
12671270
})
12681271

1269-
it('should update disabled query when updated with invalidateQueries', async () => {
1272+
it('should not update disabled query when refetched with refetchQueries', async () => {
12701273
const key = queryKey()
12711274
const states: UseQueryResult<number>[] = []
12721275
let count = 0
@@ -1295,27 +1298,15 @@ describe('useQuery', () => {
12951298

12961299
renderWithClient(queryClient, <Page />)
12971300

1298-
await sleep(100)
1301+
await sleep(50)
12991302

1300-
expect(states.length).toBe(3)
1303+
expect(states.length).toBe(1)
13011304
expect(states[0]).toMatchObject({
13021305
data: undefined,
13031306
isFetching: false,
13041307
isSuccess: false,
13051308
isStale: true,
13061309
})
1307-
expect(states[1]).toMatchObject({
1308-
data: undefined,
1309-
isFetching: true,
1310-
isSuccess: false,
1311-
isStale: true,
1312-
})
1313-
expect(states[2]).toMatchObject({
1314-
data: 1,
1315-
isFetching: false,
1316-
isSuccess: true,
1317-
isStale: true,
1318-
})
13191310
})
13201311

13211312
it('should not refetch disabled query when invalidated with invalidateQueries', async () => {

0 commit comments

Comments
 (0)