Skip to content

Commit

Permalink
fix(query-core): allow multiple roots to share same QueryClient (#4636)
Browse files Browse the repository at this point in the history
* fix(query-core): allow multiple roots to share same QueryClient

If two roots shared the `QueryClient` instance, then it would never unsubscribe from online/focus manager listeners and would also forever receive duplicate events because the unsubscriber functions would get "forgotten".

These issues would be especially noticeable on React Native apps where multiple roots that share the same `QueryClient` are common.

* test(QueryClient): Add test for multiple mounts, single unmount

Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
  • Loading branch information
appden and TkDodo committed Jan 4, 2023
1 parent 70ddaa9 commit 05a61b1
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 0 deletions.
11 changes: 11 additions & 0 deletions packages/query-core/src/queryClient.ts
Expand Up @@ -62,6 +62,7 @@ export class QueryClient {
private defaultOptions: DefaultOptions
private queryDefaults: QueryDefaults[]
private mutationDefaults: MutationDefaults[]
private mountCount: number
private unsubscribeFocus?: () => void
private unsubscribeOnline?: () => void

Expand All @@ -72,6 +73,7 @@ export class QueryClient {
this.defaultOptions = config.defaultOptions || {}
this.queryDefaults = []
this.mutationDefaults = []
this.mountCount = 0

if (process.env.NODE_ENV !== 'production' && config.logger) {
this.logger.error(
Expand All @@ -81,6 +83,9 @@ export class QueryClient {
}

mount(): void {
this.mountCount++
if (this.mountCount !== 1) return

this.unsubscribeFocus = focusManager.subscribe(() => {
if (focusManager.isFocused()) {
this.resumePausedMutations()
Expand All @@ -96,8 +101,14 @@ export class QueryClient {
}

unmount(): void {
this.mountCount--
if (this.mountCount !== 0) return

this.unsubscribeFocus?.()
this.unsubscribeFocus = undefined

this.unsubscribeOnline?.()
this.unsubscribeOnline = undefined
}

isFetching(filters?: QueryFilters): number
Expand Down
70 changes: 70 additions & 0 deletions packages/query-core/src/tests/queryClient.test.tsx
Expand Up @@ -23,6 +23,7 @@ describe('queryClient', () => {

afterEach(() => {
queryClient.clear()
queryClient.unmount()
})

describe('defaultOptions', () => {
Expand Down Expand Up @@ -1466,6 +1467,75 @@ describe('queryClient', () => {
mutationCacheResumePausedMutationsSpy.mockRestore()
onlineManager.setOnline(undefined)
})

test('should notify queryCache and mutationCache after multiple mounts and single unmount', async () => {
const testClient = createQueryClient()
testClient.mount()
testClient.mount()
testClient.unmount()

const queryCacheOnFocusSpy = jest.spyOn(
testClient.getQueryCache(),
'onFocus',
)
const queryCacheOnOnlineSpy = jest.spyOn(
testClient.getQueryCache(),
'onOnline',
)
const mutationCacheResumePausedMutationsSpy = jest.spyOn(
testClient.getMutationCache(),
'resumePausedMutations',
)

onlineManager.setOnline(true)
expect(queryCacheOnOnlineSpy).toHaveBeenCalledTimes(1)
expect(mutationCacheResumePausedMutationsSpy).toHaveBeenCalledTimes(1)

focusManager.setFocused(true)
expect(queryCacheOnFocusSpy).toHaveBeenCalledTimes(1)
expect(mutationCacheResumePausedMutationsSpy).toHaveBeenCalledTimes(2)

queryCacheOnFocusSpy.mockRestore()
queryCacheOnOnlineSpy.mockRestore()
mutationCacheResumePausedMutationsSpy.mockRestore()
focusManager.setFocused(undefined)
onlineManager.setOnline(undefined)
})

test('should not notify queryCache and mutationCache after multiple mounts/unmounts', async () => {
const testClient = createQueryClient()
testClient.mount()
testClient.mount()
testClient.unmount()
testClient.unmount()

const queryCacheOnFocusSpy = jest.spyOn(
testClient.getQueryCache(),
'onFocus',
)
const queryCacheOnOnlineSpy = jest.spyOn(
testClient.getQueryCache(),
'onOnline',
)
const mutationCacheResumePausedMutationsSpy = jest.spyOn(
testClient.getMutationCache(),
'resumePausedMutations',
)

onlineManager.setOnline(true)
expect(queryCacheOnOnlineSpy).not.toHaveBeenCalled()
expect(mutationCacheResumePausedMutationsSpy).not.toHaveBeenCalled()

focusManager.setFocused(true)
expect(queryCacheOnFocusSpy).not.toHaveBeenCalled()
expect(mutationCacheResumePausedMutationsSpy).not.toHaveBeenCalled()

queryCacheOnFocusSpy.mockRestore()
queryCacheOnOnlineSpy.mockRestore()
mutationCacheResumePausedMutationsSpy.mockRestore()
focusManager.setFocused(undefined)
onlineManager.setOnline(undefined)
})
})

describe('setMutationDefaults', () => {
Expand Down

0 comments on commit 05a61b1

Please sign in to comment.