Repository: tannerlinsley/react-query
Pull request #821 (2020-08-01 13:58:07)

test: prevent queries from interfering with each other

vercel[bot]: This pull request is being automatically deployed with Vercel ([learn more](https://vercel.link/github-learn-more)).
To see the status of your deployment, click on the icon next to each commit.

Patch:
diff --git a/src/core/query.ts b/src/core/query.ts
index e9fe36011..7dee1df88 100644
--- a/src/core/query.ts
+++ b/src/core/query.ts
@@ -154,10 +154,10 @@ export class Query<TResult, TError> {
           infiniteData[infiniteData.length - 1],
           infiniteData
         )
-        this.state.canFetchMore = this.fetchMoreVariable !== false
+        this.state.canFetchMore = Boolean(this.fetchMoreVariable)
       }
 
-      // Here we seed the pageVariabes for the query
+      // Here we seed the pageVariables for the query
       if (!this.pageVariables) {
         this.pageVariables = [[...this.queryKey]]
       }
@@ -460,7 +460,7 @@ export class Query<TResult, TError> {
           data[data.length - 1],
           data
         )
-        this.state.canFetchMore = this.fetchMoreVariable !== false
+        this.state.canFetchMore = Boolean(this.fetchMoreVariable)
         this.pageVariables = rebuiltPageVariables
 
         return (data as unknown) as TResult
@@ -497,7 +497,7 @@ export class Query<TResult, TError> {
             }
 
             this.fetchMoreVariable = infiniteConfig.getFetchMore(newData, data)
-            this.state.canFetchMore = this.fetchMoreVariable !== false
+            this.state.canFetchMore = Boolean(this.fetchMoreVariable)
 
             return (data as unknown) as TResult
           } finally {


Test patch:
diff --git a/src/core/tests/queryCache.test.tsx b/src/core/tests/queryCache.test.tsx
index 070958bff..b13b773f4 100644
--- a/src/core/tests/queryCache.test.tsx
+++ b/src/core/tests/queryCache.test.tsx
@@ -1,38 +1,37 @@
-import { sleep } from './utils'
-import { queryCache as defaultQueryCache, queryCaches } from '../'
+import { sleep, queryKey } from '../../react/tests/utils'
+import { queryCache as defaultQueryCache } from '../'
 import { makeQueryCache } from '../queryCache'
 
 describe('queryCache', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   test('setQueryData does not crash if query could not be found', () => {
+    const key = queryKey()
+
     const user = { userId: 1 }
     expect(() =>
-      defaultQueryCache.setQueryData(
-        ['USER', user],
-        (prevUser?: typeof user) => ({
-          ...prevUser!,
-          name: 'Edvin',
-        })
-      )
+      defaultQueryCache.setQueryData([key, user], (prevUser?: typeof user) => ({
+        ...prevUser!,
+        name: 'Edvin',
+      }))
     ).not.toThrow()
   })
 
   test('setQueryData does not crash when variable is null', () => {
-    defaultQueryCache.setQueryData(['USER', { userId: null }], 'Old Data')
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData([key, { userId: null }], 'Old Data')
 
     expect(() =>
-      defaultQueryCache.setQueryData(['USER', { userId: null }], 'New Data')
+      defaultQueryCache.setQueryData([key, { userId: null }], 'New Data')
     ).not.toThrow()
   })
 
   // https://github.com/tannerlinsley/react-query/issues/652
   test('prefetchQuery should not retry by default', async () => {
+    const key = queryKey()
+
     await expect(
       defaultQueryCache.prefetchQuery(
-        'key',
+        key,
         async () => {
           throw new Error('error')
         },
@@ -43,18 +42,22 @@ describe('queryCache', () => {
   })
 
   test('prefetchQuery returns the cached data on cache hits', async () => {
+    const key = queryKey()
+
     const fetchFn = () => Promise.resolve('data')
-    const first = await defaultQueryCache.prefetchQuery('key', fetchFn)
-    const second = await defaultQueryCache.prefetchQuery('key', fetchFn)
+    const first = await defaultQueryCache.prefetchQuery(key, fetchFn)
+    const second = await defaultQueryCache.prefetchQuery(key, fetchFn)
 
     expect(second).toBe(first)
   })
 
   test('prefetchQuery should not force fetch', async () => {
-    defaultQueryCache.setQueryData('key', 'og', { staleTime: 100 })
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData(key, 'og', { staleTime: 100 })
     const fetchFn = () => Promise.resolve('new')
     const first = await defaultQueryCache.prefetchQuery(
-      'key',
+      key,
       fetchFn,
       {
         initialData: 'initial',
@@ -67,9 +70,11 @@ describe('queryCache', () => {
   })
 
   test('prefetchQuery should throw error when throwOnError is true', async () => {
+    const key = queryKey()
+
     await expect(
       defaultQueryCache.prefetchQuery(
-        'key',
+        key,
         async () => {
           throw new Error('error')
         },
@@ -82,11 +87,13 @@ describe('queryCache', () => {
   })
 
   test('should notify listeners when new query is added', async () => {
+    const key = queryKey()
+
     const callback = jest.fn()
 
     defaultQueryCache.subscribe(callback)
 
-    defaultQueryCache.prefetchQuery('test', () => 'data')
+    defaultQueryCache.prefetchQuery(key, () => 'data')
 
     await sleep(100)
 
@@ -94,12 +101,14 @@ describe('queryCache', () => {
   })
 
   test('should include the queryCache and query when notifying listeners', async () => {
+    const key = queryKey()
+
     const callback = jest.fn()
 
     defaultQueryCache.subscribe(callback)
 
-    defaultQueryCache.prefetchQuery('test', () => 'data')
-    const query = defaultQueryCache.getQuery('test')
+    defaultQueryCache.prefetchQuery(key, () => 'data')
+    const query = defaultQueryCache.getQuery(key)
 
     await sleep(100)
 
@@ -107,11 +116,13 @@ describe('queryCache', () => {
   })
 
   test('should notify subscribers when new query with initialData is added', async () => {
+    const key = queryKey()
+
     const callback = jest.fn()
 
     defaultQueryCache.subscribe(callback)
 
-    defaultQueryCache.prefetchQuery('test', () => 'data', {
+    defaultQueryCache.prefetchQuery(key, () => 'data', {
       initialData: 'initial',
     })
 
@@ -121,107 +132,128 @@ describe('queryCache', () => {
   })
 
   test('setQueryData creates a new query if query was not found', () => {
-    defaultQueryCache.setQueryData('foo', 'bar')
+    const key = queryKey()
 
-    expect(defaultQueryCache.getQueryData('foo')).toBe('bar')
+    defaultQueryCache.setQueryData(key, 'bar')
+
+    expect(defaultQueryCache.getQueryData(key)).toBe('bar')
   })
 
   test('setQueryData creates a new query if query was not found', () => {
-    defaultQueryCache.setQueryData('baz', 'qux')
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData(key, 'qux')
 
-    expect(defaultQueryCache.getQueryData('baz')).toBe('qux')
+    expect(defaultQueryCache.getQueryData(key)).toBe('qux')
   })
 
   test('removeQueries does not crash when exact is provided', async () => {
+    const key = queryKey()
+
     const fetchFn = () => Promise.resolve('data')
 
     // check the query was added to the cache
-    await defaultQueryCache.prefetchQuery('key', fetchFn)
-    expect(defaultQueryCache.getQuery('key')).toBeTruthy()
+    await defaultQueryCache.prefetchQuery(key, fetchFn)
+    expect(defaultQueryCache.getQuery(key)).toBeTruthy()
 
     // check the error doesn't occur
     expect(() =>
-      defaultQueryCache.removeQueries('key', { exact: true })
+      defaultQueryCache.removeQueries(key, { exact: true })
     ).not.toThrow()
 
     // check query was successful removed
-    expect(defaultQueryCache.getQuery('key')).toBeFalsy()
+    expect(defaultQueryCache.getQuery(key)).toBeFalsy()
   })
 
   test('setQueryData should schedule stale timeout, if staleTime is set', async () => {
-    defaultQueryCache.setQueryData('key', 'test data', { staleTime: 10 })
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData(key, 'test data', { staleTime: 10 })
     // @ts-expect-error
-    expect(defaultQueryCache.getQuery('key')!.staleTimeout).not.toBeUndefined()
+    expect(defaultQueryCache.getQuery(key)!.staleTimeout).not.toBeUndefined()
   })
 
   test('setQueryData should not schedule stale timeout by default', async () => {
-    defaultQueryCache.setQueryData('key', 'test data')
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData(key, 'test data')
     // @ts-expect-error
-    expect(defaultQueryCache.getQuery('key')!.staleTimeout).toBeUndefined()
+    expect(defaultQueryCache.getQuery(key)!.staleTimeout).toBeUndefined()
   })
 
   test('setQueryData should not schedule stale timeout, if staleTime is set to `Infinity`', async () => {
-    defaultQueryCache.setQueryData('key', 'test data', { staleTime: Infinity })
+    const key = queryKey()
+
+    defaultQueryCache.setQueryData(key, 'test data', { staleTime: Infinity })
     // @ts-expect-error
-    expect(defaultQueryCache.getQuery('key')!.staleTimeout).toBeUndefined()
+    expect(defaultQueryCache.getQuery(key)!.staleTimeout).toBeUndefined()
   })
 
   test('setQueryData schedules stale timeouts appropriately', async () => {
-    defaultQueryCache.setQueryData('key', 'test data', { staleTime: 100 })
+    const key = queryKey()
 
-    expect(defaultQueryCache.getQuery('key')!.state.data).toEqual('test data')
-    expect(defaultQueryCache.getQuery('key')!.state.isStale).toEqual(false)
+    defaultQueryCache.setQueryData(key, 'test data', { staleTime: 100 })
+
+    expect(defaultQueryCache.getQuery(key)!.state.data).toEqual('test data')
+    expect(defaultQueryCache.getQuery(key)!.state.isStale).toEqual(false)
 
     await new Promise(resolve => setTimeout(resolve, 100))
 
-    expect(defaultQueryCache.getQuery('key')!.state.isStale).toEqual(true)
+    expect(defaultQueryCache.getQuery(key)!.state.isStale).toEqual(true)
   })
 
   test('setQueryData updater function works as expected', () => {
+    const key = queryKey()
+
     const updater = jest.fn(oldData => `new data + ${oldData}`)
 
-    defaultQueryCache.setQueryData('updater', 'test data')
-    defaultQueryCache.setQueryData('updater', updater)
+    defaultQueryCache.setQueryData(key, 'test data')
+    defaultQueryCache.setQueryData(key, updater)
 
     expect(updater).toHaveBeenCalled()
-    expect(defaultQueryCache.getQuery('updater')!.state.data).toEqual(
+    expect(defaultQueryCache.getQuery(key)!.state.data).toEqual(
       'new data + test data'
     )
   })
 
   test('getQueries should return queries that partially match queryKey', async () => {
+    const key1 = queryKey()
+    const key2 = queryKey()
+
     const fetchData1 = () => Promise.resolve('data1')
     const fetchData2 = () => Promise.resolve('data2')
     const fetchDifferentData = () => Promise.resolve('data3')
-    await defaultQueryCache.prefetchQuery(['data', { page: 1 }], fetchData1)
-    await defaultQueryCache.prefetchQuery(['data', { page: 2 }], fetchData2)
-    await defaultQueryCache.prefetchQuery(['differentData'], fetchDifferentData)
-    const queries = defaultQueryCache.getQueries('data')
+    await defaultQueryCache.prefetchQuery([key1, { page: 1 }], fetchData1)
+    await defaultQueryCache.prefetchQuery([key1, { page: 2 }], fetchData2)
+    await defaultQueryCache.prefetchQuery([key2], fetchDifferentData)
+    const queries = defaultQueryCache.getQueries(key1)
     const data = queries.map(query => query.state.data)
     expect(data).toEqual(['data1', 'data2'])
   })
 
   test('stale timeout dispatch is not called if query is no longer in the query cache', async () => {
-    const queryKey = 'key'
+    const key = queryKey()
+
     const fetchData = () => Promise.resolve('data')
-    await defaultQueryCache.prefetchQuery(queryKey, fetchData, {
+    await defaultQueryCache.prefetchQuery(key, fetchData, {
       staleTime: 100,
     })
-    const query = defaultQueryCache.getQuery(queryKey)
+    const query = defaultQueryCache.getQuery(key)
     expect(query!.state.isStale).toBe(false)
-    defaultQueryCache.removeQueries(queryKey)
+    defaultQueryCache.removeQueries(key)
     await sleep(50)
     expect(query!.state.isStale).toBe(false)
   })
 
   test('query interval is cleared when unsubscribed to a refetchInterval query', async () => {
-    const queryKey = 'key'
+    const key = queryKey()
+
     const fetchData = () => Promise.resolve('data')
-    await defaultQueryCache.prefetchQuery(queryKey, fetchData, {
+    await defaultQueryCache.prefetchQuery(key, fetchData, {
       cacheTime: 0,
       refetchInterval: 1,
     })
-    const query = defaultQueryCache.getQuery(queryKey)!
+    const query = defaultQueryCache.getQuery(key)!
     const instance = query.subscribe()
     instance.updateConfig(query.config)
     // @ts-expect-error
@@ -230,79 +262,88 @@ describe('queryCache', () => {
     // @ts-expect-error
     expect(instance.refetchIntervalId).toBeUndefined()
     await sleep(10)
-    expect(defaultQueryCache.getQuery(queryKey)).toBeUndefined()
+    expect(defaultQueryCache.getQuery(key)).toBeUndefined()
   })
 
   test('query is garbage collected when unsubscribed to', async () => {
-    const queryKey = 'key'
+    const key = queryKey()
+
     const fetchData = () => Promise.resolve('data')
-    await defaultQueryCache.prefetchQuery(queryKey, fetchData, { cacheTime: 0 })
-    const query = defaultQueryCache.getQuery(queryKey)!
-    expect(query.state.markedForGarbageCollection).toBe(false)
+    await defaultQueryCache.prefetchQuery(key, fetchData, { cacheTime: 0 })
+    expect(defaultQueryCache.getQuery(key)).toBeDefined()
+    const query = defaultQueryCache.getQuery(key)!
     const instance = query.subscribe()
     instance.unsubscribe()
-    expect(query.state.markedForGarbageCollection).toBe(true)
-    await sleep(10)
-    expect(defaultQueryCache.getQuery(queryKey)).toBeUndefined()
+    expect(defaultQueryCache.getQuery(key)).toBeDefined()
+    await sleep(100)
+    expect(defaultQueryCache.getQuery(key)).toBeUndefined()
   })
 
   test('query is not garbage collected unless markedForGarbageCollection is true', async () => {
-    const queryKey = 'key'
+    const key = queryKey()
+
     const fetchData = () => Promise.resolve(undefined)
-    await defaultQueryCache.prefetchQuery(queryKey, fetchData, { cacheTime: 0 })
-    const query = defaultQueryCache.getQuery(queryKey)!
-    expect(query.state.markedForGarbageCollection).toBe(false)
+    await defaultQueryCache.prefetchQuery(key, fetchData, { cacheTime: 0 })
+    expect(defaultQueryCache.getQuery(key)).toBeDefined()
+    const query = defaultQueryCache.getQuery(key)!
     const instance = query.subscribe()
+    await sleep(100)
+    expect(defaultQueryCache.getQuery(key)).toBeDefined()
     instance.unsubscribe()
-    expect(query.state.markedForGarbageCollection).toBe(true)
-    defaultQueryCache.clear({ notify: false })
-    defaultQueryCache.setQueryData(queryKey, 'data')
-    await sleep(10)
-    const newQuery = defaultQueryCache.getQuery(queryKey)!
-    expect(newQuery.state.markedForGarbageCollection).toBe(false)
-    expect(newQuery.state.data).toBe('data')
+    await sleep(100)
+    expect(defaultQueryCache.getQuery(key)).toBeUndefined()
+    defaultQueryCache.setQueryData(key, 'data')
+    await sleep(100)
+    expect(defaultQueryCache.getQuery(key)).toBeDefined()
   })
 
   describe('makeQueryCache', () => {
     test('merges defaultConfig so providing a queryFn does not overwrite the default queryKeySerializerFn', async () => {
+      const key = queryKey()
+
       const queryFn = () => 'data'
       const queryCache = makeQueryCache({
         defaultConfig: { queries: { queryFn } },
       })
 
-      expect(() => queryCache.buildQuery('test')).not.toThrow(
+      expect(() => queryCache.buildQuery(key)).not.toThrow(
         'config.queryKeySerializerFn is not a function'
       )
     })
 
     test('merges defaultConfig when query is added to cache', async () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({
         defaultConfig: {
           queries: { refetchOnMount: false, staleTime: Infinity },
         },
       })
 
-      const queryKey = 'key'
       const fetchData = () => Promise.resolve(undefined)
-      await queryCache.prefetchQuery(queryKey, fetchData)
-      const newQuery = queryCache.getQuery(queryKey)
+      await queryCache.prefetchQuery(key, fetchData)
+      const newQuery = queryCache.getQuery(key)
       expect(newQuery?.config.staleTime).toBe(Infinity)
       expect(newQuery?.config.refetchOnMount).toBe(false)
     })
 
     test('built queries are referencing the correct queryCache', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache()
-      const query = queryCache.buildQuery('test')
+      const query = queryCache.buildQuery(key)
 
       expect(query.queryCache).toBe(queryCache)
     })
 
     test('notifyGlobalListeners passes the same instance', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache()
       const subscriber = jest.fn()
       const unsubscribe = queryCache.subscribe(subscriber)
-      const query = queryCache.buildQuery('test')
-      query.setData('foo');
+      const query = queryCache.buildQuery(key)
+      query.setData('foo')
       expect(subscriber).toHaveBeenCalledWith(queryCache, query)
 
       unsubscribe()
diff --git a/src/core/tests/utils.test.tsx b/src/core/tests/utils.test.tsx
index cca235627..cb1e0ad59 100644
--- a/src/core/tests/utils.test.tsx
+++ b/src/core/tests/utils.test.tsx
@@ -1,12 +1,11 @@
-import { setConsole, queryCache, queryCaches } from '../'
+import { setConsole, queryCache } from '../'
 import { deepEqual, shallowEqual } from '../utils'
+import { queryKey } from '../../react/tests/utils'
 
 describe('core/utils', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('setConsole should override Console object', async () => {
+    const key = queryKey()
+
     const mockConsole = {
       error: jest.fn(),
       log: jest.fn(),
@@ -16,7 +15,7 @@ describe('core/utils', () => {
     setConsole(mockConsole)
 
     await queryCache.prefetchQuery(
-      'key',
+      key,
       async () => {
         throw new Error('Test')
       },
diff --git a/src/core/tests/utils.tsx b/src/core/tests/utils.tsx
deleted file mode 100644
index 1a3a619a2..000000000
--- a/src/core/tests/utils.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-export function sleep(timeout: number): Promise<void> {
-  return new Promise((resolve, _reject) => {
-    setTimeout(resolve, timeout)
-  })
-}
diff --git a/src/react/tests/ReactQueryCacheProvider.test.tsx b/src/react/tests/ReactQueryCacheProvider.test.tsx
index 1dbc774db..1760ab405 100644
--- a/src/react/tests/ReactQueryCacheProvider.test.tsx
+++ b/src/react/tests/ReactQueryCacheProvider.test.tsx
@@ -6,19 +6,16 @@ import {
   queryCache,
   useQuery,
   useQueryCache,
-  queryCaches,
 } from '../index'
-import { sleep } from './utils'
+import { sleep, queryKey } from './utils'
 import { QueryCache } from '../../core/queryCache'
 
 describe('ReactQueryCacheProvider', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   test('when not used, falls back to global cache', async () => {
+    const key = queryKey()
+
     function Page() {
-      const { data } = useQuery('test', async () => {
+      const { data } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -34,14 +31,16 @@ describe('ReactQueryCacheProvider', () => {
 
     await waitFor(() => rendered.getByText('test'))
 
-    expect(queryCache.getQuery('test')).toBeDefined()
+    expect(queryCache.getQuery(key)).toBeDefined()
   })
 
   test('sets a specific cache for all queries to use', async () => {
+    const key = queryKey()
+
     const cache = makeQueryCache()
 
     function Page() {
-      const { data } = useQuery('test', async () => {
+      const { data } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -61,14 +60,15 @@ describe('ReactQueryCacheProvider', () => {
 
     await waitFor(() => rendered.getByText('test'))
 
-    expect(queryCache.getQuery('test')).not.toBeDefined()
-    expect(cache.getQuery('test')).toBeDefined()
-    cache.clear({ notify: false })
+    expect(queryCache.getQuery(key)).not.toBeDefined()
+    expect(cache.getQuery(key)).toBeDefined()
   })
 
   test('implicitly creates a new cache for all queries to use', async () => {
+    const key = queryKey()
+
     function Page() {
-      const { data } = useQuery('test', async () => {
+      const { data } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -88,15 +88,18 @@ describe('ReactQueryCacheProvider', () => {
 
     await waitFor(() => rendered.getByText('test'))
 
-    expect(queryCache.getQuery('test')).not.toBeDefined()
+    expect(queryCache.getQuery(key)).not.toBeDefined()
   })
 
   test('allows multiple caches to be partitioned', async () => {
+    const key1 = queryKey()
+    const key2 = queryKey()
+
     const cache1 = makeQueryCache()
     const cache2 = makeQueryCache()
 
     function Page1() {
-      const { data } = useQuery('test1', async () => {
+      const { data } = useQuery(key1, async () => {
         await sleep(10)
         return 'test1'
       })
@@ -108,7 +111,7 @@ describe('ReactQueryCacheProvider', () => {
       )
     }
     function Page2() {
-      const { data } = useQuery('test2', async () => {
+      const { data } = useQuery(key2, async () => {
         await sleep(10)
         return 'test2'
       })
@@ -134,16 +137,18 @@ describe('ReactQueryCacheProvider', () => {
     await waitFor(() => rendered.getByText('test1'))
     await waitFor(() => rendered.getByText('test2'))
 
-    expect(cache1.getQuery('test1')).toBeDefined()
-    expect(cache1.getQuery('test2')).not.toBeDefined()
-    expect(cache2.getQuery('test1')).not.toBeDefined()
-    expect(cache2.getQuery('test2')).toBeDefined()
+    expect(cache1.getQuery(key1)).toBeDefined()
+    expect(cache1.getQuery(key2)).not.toBeDefined()
+    expect(cache2.getQuery(key1)).not.toBeDefined()
+    expect(cache2.getQuery(key2)).toBeDefined()
 
     cache1.clear({ notify: false })
     cache2.clear({ notify: false })
   })
 
   test('when cache changes, previous cache is cleaned', () => {
+    const key = queryKey()
+
     const caches: QueryCache[] = []
     const customCache = makeQueryCache()
 
@@ -153,7 +158,7 @@ describe('ReactQueryCacheProvider', () => {
         caches.push(queryCache)
       }, [queryCache])
 
-      const { data } = useQuery('test', async () => {
+      const { data } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -185,7 +190,9 @@ describe('ReactQueryCacheProvider', () => {
     customCache.clear({ notify: false })
   })
 
-  test('uses defaultConfig for queries when they don\'t provide their own config', async () => {
+  test("uses defaultConfig for queries when they don't provide their own config", async () => {
+    const key = queryKey()
+
     const cache = makeQueryCache({
       defaultConfig: {
         queries: {
@@ -195,7 +202,7 @@ describe('ReactQueryCacheProvider', () => {
     })
 
     function Page() {
-      const { data } = useQuery('test', async () => {
+      const { data } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -215,8 +222,8 @@ describe('ReactQueryCacheProvider', () => {
 
     await waitFor(() => rendered.getByText('test'))
 
-    expect(cache.getQuery('test')).toBeDefined()
-    expect(cache.getQuery('test')?.config.staleTime).toBe(Infinity)
+    expect(cache.getQuery(key)).toBeDefined()
+    expect(cache.getQuery(key)?.config.staleTime).toBe(Infinity)
     cache.clear({ notify: false })
   })
 })
diff --git a/src/react/tests/ReactQueryConfigProvider.test.tsx b/src/react/tests/ReactQueryConfigProvider.test.tsx
index 612598fd1..0c5c3c70d 100644
--- a/src/react/tests/ReactQueryConfigProvider.test.tsx
+++ b/src/react/tests/ReactQueryConfigProvider.test.tsx
@@ -1,21 +1,14 @@
 import React, { useState } from 'react'
 import { act, fireEvent, render, waitFor } from '@testing-library/react'
-import {
-  ReactQueryConfigProvider,
-  useQuery,
-  queryCache,
-  queryCaches,
-} from '../index'
+import { ReactQueryConfigProvider, useQuery, queryCache } from '../index'
 
-import { sleep } from './utils'
+import { sleep, queryKey } from './utils'
 
 describe('ReactQueryConfigProvider', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   // // See https://github.com/tannerlinsley/react-query/issues/105
   it('should allow overriding the config', async () => {
+    const key = queryKey()
+
     const onSuccess = jest.fn()
 
     const config = {
@@ -25,7 +18,7 @@ describe('ReactQueryConfigProvider', () => {
     }
 
     function Page() {
-      const { status } = useQuery('test', async () => {
+      const { status } = useQuery(key, async () => {
         await sleep(10)
         return 'data'
       })
@@ -49,6 +42,8 @@ describe('ReactQueryConfigProvider', () => {
   })
 
   it('should allow overriding the default config from the outermost provider', async () => {
+    const key = queryKey()
+
     const outerConfig = {
       queries: {
         queryFn: jest.fn(async () => {
@@ -81,17 +76,19 @@ describe('ReactQueryConfigProvider', () => {
 
     await waitFor(() => rendered.getByText('Placeholder'))
 
-    await queryCache.prefetchQuery('test')
+    await queryCache.prefetchQuery(key)
 
-    expect(outerConfig.queries.queryFn).toHaveBeenCalledWith('test')
+    expect(outerConfig.queries.queryFn).toHaveBeenCalledWith(key)
     expect(innerConfig.queries.queryFn).not.toHaveBeenCalled()
 
-    const data = queryCache.getQueryData('test')
+    const data = queryCache.getQueryData(key)
 
     expect(data).toEqual('outer')
   })
 
   it('should reset to defaults when unmounted', async () => {
+    const key = queryKey()
+
     const onSuccess = jest.fn()
 
     const config = {
@@ -125,7 +122,7 @@ describe('ReactQueryConfigProvider', () => {
     }
 
     function Page() {
-      const { data } = useQuery('test', queryFn)
+      const { data } = useQuery(key, queryFn)
 
       return (
         <div>
@@ -139,7 +136,7 @@ describe('ReactQueryConfigProvider', () => {
     await waitFor(() => rendered.getByText('Data: none'))
 
     act(() => {
-      queryCache.prefetchQuery('test', queryFn)
+      queryCache.prefetchQuery(key, queryFn)
     })
 
     await waitFor(() => rendered.getByText('Data: data'))
@@ -149,8 +146,6 @@ describe('ReactQueryConfigProvider', () => {
     fireEvent.click(rendered.getByText('unmount'))
 
     act(() => {
-      // wipe query cache/stored config
-      queryCache.clear({ notify: false })
       onSuccess.mockClear()
     })
 
@@ -158,6 +153,8 @@ describe('ReactQueryConfigProvider', () => {
   })
 
   it('should reset to previous config when nested provider is unmounted', async () => {
+    const key = queryKey()
+
     let counterRef = 0
     const parentOnSuccess = jest.fn()
 
@@ -184,7 +181,7 @@ describe('ReactQueryConfigProvider', () => {
     }
 
     function Component() {
-      const { data, refetch } = useQuery('test', queryFn)
+      const { data, refetch } = useQuery(key, queryFn)
 
       return (
         <div>
diff --git a/src/react/tests/ssr.test.tsx b/src/react/tests/ssr.test.tsx
index 777f2f201..4ee457d85 100644
--- a/src/react/tests/ssr.test.tsx
+++ b/src/react/tests/ssr.test.tsx
@@ -12,7 +12,7 @@ import {
   makeQueryCache,
   useQuery,
 } from '../index'
-import { sleep } from './utils'
+import { sleep, queryKey } from './utils'
 
 describe('Server Side Rendering', () => {
   // A frozen cache does not cache any data. This is the default
@@ -22,13 +22,13 @@ describe('Server Side Rendering', () => {
   //
   // See https://github.com/tannerlinsley/react-query/issues/70
   it('global cache should be frozen by default', async () => {
+    const key = queryKey()
+
     const fetchFn = () => Promise.resolve('data')
-    const data = await queryCache.prefetchQuery('key', fetchFn)
+    const data = await queryCache.prefetchQuery(key, fetchFn)
 
     expect(data).toBe('data')
-    expect(queryCache.getQuery('key')).toBeFalsy()
-
-    queryCache.clear({ notify: false })
+    expect(queryCache.getQuery(key)).toBeFalsy()
   })
 
   // When consumers of the library create a cache explicitly by
@@ -36,21 +36,25 @@ describe('Server Side Rendering', () => {
   // not using that cache to cache data between requests or do so
   // in a safe way.
   it('created caches should be unfrozen by default', async () => {
+    const key = queryKey()
+
     const queryCache = makeQueryCache()
     const fetchFn = () => Promise.resolve('data')
-    const data = await queryCache.prefetchQuery('key', fetchFn)
+    const data = await queryCache.prefetchQuery(key, fetchFn)
 
     expect(data).toBe('data')
-    expect(queryCache.getQuery('key')).toBeTruthy()
+    expect(queryCache.getQuery(key)).toBeTruthy()
   })
 
   describe('frozen cache', () => {
     it('should not trigger fetch', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: true })
       const queryFn = jest.fn()
 
       function Page() {
-        const query = useQuery('test', queryFn)
+        const query = useQuery(key, queryFn)
 
         const content = `status ${query.status}`
 
@@ -72,12 +76,14 @@ describe('Server Side Rendering', () => {
     })
 
     it('should not add initialData to the cache', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: true })
 
       function Page() {
         const [page, setPage] = React.useState(1)
         const { resolvedData } = usePaginatedQuery(
-          ['data', page],
+          [key, page],
           async (_queryName: string, page: number) => {
             return page
           },
@@ -98,22 +104,26 @@ describe('Server Side Rendering', () => {
     })
 
     it('should not add prefetched data to the cache', async () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: true })
       const fetchFn = () => Promise.resolve('data')
-      const data = await queryCache.prefetchQuery('key', fetchFn)
+      const data = await queryCache.prefetchQuery(key, fetchFn)
 
       expect(data).toBe('data')
-      expect(queryCache.getQuery('key')).toBeFalsy()
+      expect(queryCache.getQuery(key)).toBeFalsy()
     })
   })
 
   describe('unfrozen cache', () => {
     it('should not trigger fetch', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: false })
       const queryFn = jest.fn()
 
       function Page() {
-        const query = useQuery('test', queryFn)
+        const query = useQuery(key, queryFn)
 
         const content = `status ${query.status}`
 
@@ -135,20 +145,24 @@ describe('Server Side Rendering', () => {
     })
 
     it('should add prefetched data to cache', async () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: false })
       const fetchFn = () => Promise.resolve('data')
-      const data = await queryCache.prefetchQuery('key', fetchFn)
+      const data = await queryCache.prefetchQuery(key, fetchFn)
 
       expect(data).toBe('data')
-      expect(queryCache.getQuery('key')?.state.data).toBe('data')
+      expect(queryCache.getQuery(key)?.state.data).toBe('data')
     })
 
     it('should return existing data from the cache', async () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: false })
       const queryFn = jest.fn(() => sleep(10))
 
       function Page() {
-        const query = useQuery('test', queryFn)
+        const query = useQuery(key, queryFn)
 
         const content = `status ${query.status}`
 
@@ -159,7 +173,7 @@ describe('Server Side Rendering', () => {
         )
       }
 
-      await queryCache.prefetchQuery('test', queryFn)
+      await queryCache.prefetchQuery(key, queryFn)
 
       const markup = renderToString(
         <ReactQueryCacheProvider queryCache={queryCache}>
@@ -172,11 +186,13 @@ describe('Server Side Rendering', () => {
     })
 
     it('should add initialData to the cache', () => {
+      const key = queryKey()
+
       const queryCache = makeQueryCache({ frozen: false })
       function Page() {
         const [page, setPage] = React.useState(1)
         const { resolvedData } = usePaginatedQuery(
-          ['data', page],
+          [key, page],
           async (_queryName: string, page: number) => {
             return page
           },
@@ -197,10 +213,12 @@ describe('Server Side Rendering', () => {
         </ReactQueryCacheProvider>
       )
 
-      expect(Object.keys(queryCache.queries)).toEqual(['["data",1]'])
+      expect(Object.keys(queryCache.queries)).toEqual([`["${key}",1]`])
     })
 
     it('should not call setTimeout', async () => {
+      const key = queryKey()
+
       // @ts-ignore
       const setTimeoutMock = jest.spyOn(global, 'setTimeout')
 
@@ -208,7 +226,7 @@ describe('Server Side Rendering', () => {
       const queryFn = jest.fn(() => Promise.resolve())
 
       function Page() {
-        const query = useQuery('test', queryFn)
+        const query = useQuery(key, queryFn)
 
         const content = `status ${query.status}`
 
@@ -219,7 +237,7 @@ describe('Server Side Rendering', () => {
         )
       }
 
-      await queryCache.prefetchQuery('test', queryFn)
+      await queryCache.prefetchQuery(key, queryFn)
 
       const markup = renderToString(
         <ReactQueryCacheProvider queryCache={queryCache}>
diff --git a/src/react/tests/suspense.test.tsx b/src/react/tests/suspense.test.tsx
index 7f7d0ebca..177733484 100644
--- a/src/react/tests/suspense.test.tsx
+++ b/src/react/tests/suspense.test.tsx
@@ -2,20 +2,18 @@ import { render, waitFor, fireEvent } from '@testing-library/react'
 import { ErrorBoundary } from 'react-error-boundary'
 import * as React from 'react'
 
-import { useQuery, queryCache, queryCaches } from '../index'
-import { sleep } from './utils'
+import { useQuery, queryCache } from '../index'
+import { sleep, queryKey } from './utils'
 
 describe("useQuery's in Suspense mode", () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('should not call the queryFn twice when used in Suspense mode', async () => {
+    const key = queryKey()
+
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => sleep(10))
 
     function Page() {
-      useQuery(['test'], queryFn, { suspense: true })
+      useQuery([key], queryFn, { suspense: true })
 
       return <>rendered</>
     }
@@ -32,10 +30,10 @@ describe("useQuery's in Suspense mode", () => {
   })
 
   it('should remove query instance when component unmounted', async () => {
-    const QUERY_KEY = 'test'
+    const key = queryKey()
 
     function Page() {
-      useQuery([QUERY_KEY], () => sleep(10), { suspense: true })
+      useQuery([key], () => sleep(10), { suspense: true })
 
       return <>rendered</>
     }
@@ -54,24 +52,26 @@ describe("useQuery's in Suspense mode", () => {
     const rendered = render(<App />)
 
     expect(rendered.queryByText('rendered')).toBeNull()
-    expect(queryCache.getQuery(QUERY_KEY)).toBeFalsy()
+    expect(queryCache.getQuery(key)).toBeFalsy()
 
     fireEvent.click(rendered.getByLabelText('toggle'))
     await waitFor(() => rendered.getByText('rendered'))
 
-    expect(queryCache.getQuery(QUERY_KEY)?.instances.length).toBe(1)
+    expect(queryCache.getQuery(key)?.instances.length).toBe(1)
 
     fireEvent.click(rendered.getByLabelText('toggle'))
 
     expect(rendered.queryByText('rendered')).toBeNull()
-    expect(queryCache.getQuery(QUERY_KEY)?.instances.length).toBe(0)
+    expect(queryCache.getQuery(key)?.instances.length).toBe(0)
   })
 
   it('should call onSuccess on the first successful call', async () => {
+    const key = queryKey()
+
     const successFn = jest.fn()
 
     function Page() {
-      useQuery(['test'], () => sleep(10), {
+      useQuery([key], () => sleep(10), {
         suspense: true,
         onSuccess: successFn,
       })
@@ -92,13 +92,15 @@ describe("useQuery's in Suspense mode", () => {
 
   // https://github.com/tannerlinsley/react-query/issues/468
   it('should reset error state if new component instances are mounted', async () => {
+    const key = queryKey()
+
     let succeed = false
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
     function Page() {
       useQuery(
-        'test',
+        key,
         async () => {
           await sleep(10)
 
@@ -154,12 +156,14 @@ describe("useQuery's in Suspense mode", () => {
   })
 
   it('should not call the queryFn when not enabled', async () => {
+    const key = queryKey()
+
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => sleep(10))
 
     function Page() {
       const [enabled, setEnabled] = React.useState(false)
-      useQuery(['test'], queryFn, { suspense: true, enabled })
+      useQuery([key], queryFn, { suspense: true, enabled })
 
       return <button aria-label="fire" onClick={() => setEnabled(true)} />
     }
diff --git a/src/react/tests/useInfiniteQuery.test.tsx b/src/react/tests/useInfiniteQuery.test.tsx
index 43327b2de..b2210c3f1 100644
--- a/src/react/tests/useInfiniteQuery.test.tsx
+++ b/src/react/tests/useInfiniteQuery.test.tsx
@@ -1,8 +1,8 @@
 import { render, waitFor, fireEvent } from '@testing-library/react'
 import * as React from 'react'
 
-import { useInfiniteQuery, useQueryCache, queryCaches } from '../index'
-import { sleep } from './utils'
+import { useInfiniteQuery, useQueryCache } from '../index'
+import { sleep, queryKey } from './utils'
 import { InfiniteQueryResult } from '../../core/types'
 
 interface Result {
@@ -31,17 +31,15 @@ const fetchItems = async (page: number, ts: number): Promise<Result> => {
 }
 
 describe('useInfiniteQuery', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('should return the correct states for a successful query', async () => {
+    const key = queryKey()
+
     let count = 0
     const states: InfiniteQueryResult<Result>[] = []
 
     function Page() {
       const state = useInfiniteQuery(
-        'items',
+        key,
         (_key: string, nextId = 0) => fetchItems(nextId, count++),
         {
           getFetchMore: (lastGroup, _allGroups) => Boolean(lastGroup.nextId),
@@ -108,6 +106,8 @@ describe('useInfiniteQuery', () => {
   })
 
   it('should allow you to fetch more pages', async () => {
+    const key = queryKey()
+
     function Page() {
       const fetchCountRef = React.useRef(0)
       const {
@@ -120,7 +120,7 @@ describe('useInfiniteQuery', () => {
         canFetchMore,
         refetch,
       } = useInfiniteQuery<Result, Error, string>(
-        'items',
+        key,
         (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
         {
           getFetchMore: (lastGroup, _allGroups) => lastGroup.nextId,
@@ -202,7 +202,7 @@ describe('useInfiniteQuery', () => {
     })
   })
 
-  it('should compute canFetchMore correctly using initialData', async () => {
+  it('should compute canFetchMore correctly for falsy getFetchMore return value', async () => {
     function Page() {
       const fetchCountRef = React.useRef(0)
       const {
@@ -217,6 +217,86 @@ describe('useInfiniteQuery', () => {
       } = useInfiniteQuery<Result, Error, string>(
         'items',
         (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
+        {
+          getFetchMore: (_lastGroup, _allGroups) => undefined,
+        }
+      )
+
+      return (
+        <div>
+          <h1>Pagination</h1>
+          {status === 'loading' ? (
+            'Loading...'
+          ) : status === 'error' ? (
+            <span>Error: {error?.message}</span>
+          ) : (
+            <>
+              <div>Data:</div>
+              {data?.map((page, i) => (
+                <div key={i}>
+                  <div>
+                    Page {i}: {page.ts}
+                  </div>
+                  <div key={i}>
+                    {page.items.map(item => (
+                      <p key={item}>Item: {item}</p>
+                    ))}
+                  </div>
+                </div>
+              ))}
+              <div>
+                <button
+                  onClick={() => fetchMore()}
+                  disabled={!canFetchMore || Boolean(isFetchingMore)}
+                >
+                  {isFetchingMore
+                    ? 'Loading more...'
+                    : canFetchMore
+                    ? 'Load More'
+                    : 'Nothing more to load'}
+                </button>
+                <button onClick={() => refetch()}>Refetch</button>
+              </div>
+              <div>
+                {isFetching && !isFetchingMore
+                  ? 'Background Updating...'
+                  : null}
+              </div>
+            </>
+          )}
+        </div>
+      )
+    }
+
+    const rendered = render(<Page />)
+
+    rendered.getByText('Loading...')
+
+    await waitFor(() => {
+      rendered.getByText('Item: 9')
+      rendered.getByText('Page 0: 0')
+    })
+
+    rendered.getByText('Nothing more to load')
+  })
+
+  it('should compute canFetchMore correctly using initialData', async () => {
+    const key = queryKey()
+
+    function Page() {
+      const fetchCountRef = React.useRef(0)
+      const {
+        status,
+        data,
+        error,
+        isFetching,
+        isFetchingMore,
+        fetchMore,
+        canFetchMore,
+        refetch,
+      } = useInfiniteQuery<Result, Error, string>(
+        key,
+        (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
         {
           initialData: [initialItems(0)],
           getFetchMore: (lastGroup, _allGroups) => lastGroup.nextId,
@@ -293,7 +373,84 @@ describe('useInfiniteQuery', () => {
     })
   })
 
+  it('should compute canFetchMore correctly for falsy getFetchMore return value using initialData', async () => {
+    function Page() {
+      const fetchCountRef = React.useRef(0)
+      const {
+        status,
+        data,
+        error,
+        isFetching,
+        isFetchingMore,
+        fetchMore,
+        canFetchMore,
+        refetch,
+      } = useInfiniteQuery<Result, Error, string>(
+        'items',
+        (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
+        {
+          initialData: [initialItems(0)],
+          getFetchMore: (_lastGroup, _allGroups) => undefined,
+        }
+      )
+
+      return (
+        <div>
+          <h1>Pagination</h1>
+          {status === 'loading' ? (
+            'Loading...'
+          ) : status === 'error' ? (
+            <span>Error: {error?.message}</span>
+          ) : (
+            <>
+              <div>Data:</div>
+              {data?.map((page, i) => (
+                <div key={i}>
+                  <div>
+                    Page {i}: {page.ts}
+                  </div>
+                  <div key={i}>
+                    {page.items.map(item => (
+                      <p key={item}>Item: {item}</p>
+                    ))}
+                  </div>
+                </div>
+              ))}
+              <div>
+                <button
+                  onClick={() => fetchMore()}
+                  disabled={!canFetchMore || Boolean(isFetchingMore)}
+                >
+                  {isFetchingMore
+                    ? 'Loading more...'
+                    : canFetchMore
+                    ? 'Load More'
+                    : 'Nothing more to load'}
+                </button>
+                <button onClick={() => refetch()}>Refetch</button>
+              </div>
+              <div>
+                {isFetching && !isFetchingMore
+                  ? 'Background Updating...'
+                  : null}
+              </div>
+            </>
+          )}
+        </div>
+      )
+    }
+
+    const rendered = render(<Page />)
+
+    rendered.getByText('Item: 9')
+    rendered.getByText('Page 0: 0')
+
+    rendered.getByText('Nothing more to load')
+  })
+
   it('should build fresh cursors on refetch', async () => {
+    const key = queryKey()
+
     const genItems = (size: number) =>
       [...new Array(size)].fill(null).map((_, d) => d)
     const items = genItems(15)
@@ -320,7 +477,7 @@ describe('useInfiniteQuery', () => {
         canFetchMore,
         refetch,
       } = useInfiniteQuery<Result, Error, string>(
-        'items',
+        key,
         (_key, nextId = 0) => fetchItems(nextId, fetchCountRef.current++),
         {
           getFetchMore: (lastGroup, _allGroups) => lastGroup.nextId,
@@ -367,7 +524,7 @@ describe('useInfiniteQuery', () => {
                     // makes an actual network request
                     // and calls invalidateQueries in an onSuccess
                     items.splice(4, 1)
-                    queryCache.invalidateQueries('items')
+                    queryCache.invalidateQueries(key)
                   }}
                 >
                   Remove item
diff --git a/src/react/tests/useIsFetching.test.tsx b/src/react/tests/useIsFetching.test.tsx
index 37833155d..0ae990b52 100644
--- a/src/react/tests/useIsFetching.test.tsx
+++ b/src/react/tests/useIsFetching.test.tsx
@@ -1,24 +1,21 @@
 import { render, fireEvent, waitFor } from '@testing-library/react'
 import * as React from 'react'
 
-import { useQuery, useIsFetching, queryCaches } from '../index'
-import { sleep } from './utils'
+import { useQuery, useIsFetching } from '../index'
+import { sleep, queryKey } from './utils'
 
 describe('useIsFetching', () => {
-  afterEach(() => {
-    // We notify false because it causes act issue if we notify useIsFetching after it's unmounted
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   // See https://github.com/tannerlinsley/react-query/issues/105
   it('should update as queries start and stop fetching', async () => {
+    const key = queryKey()
+
     function Page() {
       const [ready, setReady] = React.useState(false)
 
       const isFetching = useIsFetching()
 
       useQuery(
-        'test',
+        key,
         async () => {
           await sleep(1000)
           return 'test'
diff --git a/src/react/tests/useMutation.test.tsx b/src/react/tests/useMutation.test.tsx
index 838113e50..d94dadeef 100644
--- a/src/react/tests/useMutation.test.tsx
+++ b/src/react/tests/useMutation.test.tsx
@@ -1,13 +1,9 @@
 import { render, fireEvent, waitFor } from '@testing-library/react'
 import * as React from 'react'
 
-import { useMutation, queryCaches } from '../index'
+import { useMutation } from '../index'
 
 describe('useMutation', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('should be able to reset `data`', async () => {
     function Page() {
       const [mutate, { data = '', reset }] = useMutation(() =>
diff --git a/src/react/tests/usePaginatedQuery.test.tsx b/src/react/tests/usePaginatedQuery.test.tsx
index 243f652ba..15ad1227f 100644
--- a/src/react/tests/usePaginatedQuery.test.tsx
+++ b/src/react/tests/usePaginatedQuery.test.tsx
@@ -1,21 +1,18 @@
 import { render, fireEvent, waitFor } from '@testing-library/react'
 import * as React from 'react'
-import { sleep } from './utils'
+import { sleep, queryKey } from './utils'
 
-import { usePaginatedQuery, queryCaches } from '../index'
+import { usePaginatedQuery } from '../index'
 import { PaginatedQueryResult } from '../../core/types'
 
 describe('usePaginatedQuery', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('should return the correct states for a successful query', async () => {
+    const key = queryKey()
     const states: PaginatedQueryResult<number>[] = []
 
     function Page() {
       const state = usePaginatedQuery(
-        ['data', 1],
+        [key, 1],
         async (_queryName, page: number) => {
           await sleep(10)
           return page
@@ -73,10 +70,12 @@ describe('usePaginatedQuery', () => {
   })
 
   it('should use previous page data while fetching the next page', async () => {
+    const key = queryKey()
+
     function Page() {
       const [page, setPage] = React.useState(1)
       const { resolvedData = 'undefined' } = usePaginatedQuery(
-        ['data', page],
+        [key, page],
         async (_queryName: string, page: number) => {
           await sleep(10)
           return page
@@ -106,12 +105,14 @@ describe('usePaginatedQuery', () => {
   })
 
   it('should use initialData only on the first page, then use previous page data while fetching the next page', async () => {
+    const key = queryKey()
+
     function Page() {
       const [page, setPage] = React.useState(1)
       const params = { page }
 
       const { resolvedData } = usePaginatedQuery(
-        ['data', params],
+        [key, params],
         async (_queryName: string, { page }: typeof params) => {
           await sleep(10)
           return page
@@ -146,12 +147,14 @@ describe('usePaginatedQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/169
   it('should not trigger unnecessary loading state', async () => {
+    const key = queryKey()
+
     function Page() {
       const [page, setPage] = React.useState(1)
       const params = { page }
 
       const { resolvedData, status } = usePaginatedQuery(
-        ['data', params],
+        [key, params],
         async (_queryName: string, { page }: typeof params) => {
           await sleep(10)
           return page
@@ -182,11 +185,13 @@ describe('usePaginatedQuery', () => {
   })
 
   it('should clear resolvedData data when query is falsy', async () => {
+    const key = queryKey()
+
     function Page() {
       const [searchTerm, setSearchTerm] = React.useState('')
       const [page, setPage] = React.useState(1)
       const { resolvedData = 'undefined' } = usePaginatedQuery(
-        ['data', searchTerm, page],
+        [key, searchTerm, page],
         async (_queryName: string, searchTerm: string, page: number) => {
           await sleep(10)
           return `${searchTerm} ${page}`
@@ -245,12 +250,14 @@ describe('usePaginatedQuery', () => {
   })
 
   it('should not suspend while fetching the next page', async () => {
+    const key = queryKey()
+
     function Page() {
       const [page, setPage] = React.useState(1)
       const params = { page }
 
       const { resolvedData } = usePaginatedQuery(
-        ['data', params],
+        [key, params],
         async (_queryName: string, { page }: typeof params) => {
           await sleep(10)
           return page
diff --git a/src/react/tests/useQuery.test.tsx b/src/react/tests/useQuery.test.tsx
index 4759865dd..80eddbb9b 100644
--- a/src/react/tests/useQuery.test.tsx
+++ b/src/react/tests/useQuery.test.tsx
@@ -1,48 +1,51 @@
 import { render, act, waitFor, fireEvent } from '@testing-library/react'
 import * as React from 'react'
 
-import { useQuery, queryCache, queryCaches } from '../index'
-import { sleep, expectType } from './utils'
+import { useQuery, queryCache } from '../index'
+import { sleep, expectType, queryKey } from './utils'
 import { QueryResult } from '../../core/types'
 
 describe('useQuery', () => {
-  afterEach(() => {
-    queryCaches.forEach(cache => cache.clear({ notify: false }))
-  })
-
   it('should return the correct types', () => {
+    const key1 = queryKey()
+    const key2 = queryKey()
+    const key3 = queryKey()
+    const key4 = queryKey()
+    const key5 = queryKey()
+    const key6 = queryKey()
+
     // @ts-ignore
     // eslint-disable-next-line
     function Page() {
       // unspecified query function should default to unknown
-      const noQueryFn = useQuery('test')
+      const noQueryFn = useQuery(key1)
       expectType<unknown>(noQueryFn.data)
       expectType<unknown>(noQueryFn.error)
 
       // it should infer the result type from the query function
-      const fromQueryFn = useQuery('test', () => 'test')
+      const fromQueryFn = useQuery(key2, () => 'test')
       expectType<string | undefined>(fromQueryFn.data)
       expectType<unknown>(fromQueryFn.error)
 
       // it should be possible to specify the error type
-      const withError = useQuery<string, Error, string>('test', () => 'test')
+      const withError = useQuery<string, Error, string>(key3, () => 'test')
       expectType<string | undefined>(withError.data)
       expectType<Error | null>(withError.error)
 
       // unspecified error type should default to unknown
-      const withoutError = useQuery<number, unknown, string>('test', () => 1)
+      const withoutError = useQuery<number, unknown, string>(key4, () => 1)
       expectType<number | undefined>(withoutError.data)
       expectType<unknown>(withoutError.error)
 
       // it should provide the result type in the configuration
-      useQuery(['key'], async () => true, {
+      useQuery([key5], async () => true, {
         onSuccess: data => expectType<boolean>(data),
         onSettled: data => expectType<boolean | undefined>(data),
       })
 
       // should error when the query function result does not match with the specified type
       // @ts-expect-error
-      useQuery<number>('test', () => 'test')
+      useQuery<number>(key6, () => 'test')
 
       // should error when a non configuration object is given as first argument
       // @ts-expect-error
@@ -52,10 +55,10 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/105
   it('should allow to set default data value', async () => {
-    const queryKey = Math.random()
+    const key = queryKey()
 
     function Page() {
-      const { data = 'default' } = useQuery(queryKey, async () => {
+      const { data = 'default' } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -75,11 +78,11 @@ describe('useQuery', () => {
   })
 
   it('should return the correct states for a successful query', async () => {
-    const queryKey = Math.random()
+    const key = queryKey()
     const states: QueryResult<string>[] = []
 
     function Page() {
-      const state = useQuery(queryKey, () => 'test')
+      const state = useQuery(key, () => 'test')
 
       states.push(state)
 
@@ -130,15 +133,15 @@ describe('useQuery', () => {
   })
 
   it('should return the correct states for an unsuccessful query', async () => {
+    const key = queryKey()
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
-    const queryKey = Math.random()
     const states: QueryResult<undefined, string>[] = []
 
     function Page() {
-      const state = useQuery<undefined, string, number>(
-        queryKey,
+      const state = useQuery<undefined, string, string>(
+        key,
         () => Promise.reject('rejected'),
         {
           retry: 1,
@@ -213,16 +216,16 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/137
   it('should not override initial data in dependent queries', async () => {
-    const queryKey1 = Math.random()
-    const queryKey2 = Math.random()
+    const key1 = queryKey()
+    const key2 = queryKey()
 
     function Page() {
-      const first = useQuery(queryKey1, () => 'data', {
+      const first = useQuery(key1, () => 'data', {
         enabled: false,
         initialData: 'init',
       })
 
-      const second = useQuery(queryKey2, () => 'data', {
+      const second = useQuery(key2, () => 'data', {
         enabled: false,
         initialData: 'init',
       })
@@ -247,11 +250,14 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/170
   it('should start with status idle if enabled is false', async () => {
+    const key1 = queryKey()
+    const key2 = queryKey()
+
     function Page() {
-      const first = useQuery('first', () => 'data', {
+      const first = useQuery(key1, () => 'data', {
         enabled: false,
       })
-      const second = useQuery('second', () => 'data')
+      const second = useQuery(key2, () => 'data')
 
       return (
         <div>
@@ -272,8 +278,10 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/144
   it('should be in "loading" state by default', async () => {
+    const key = queryKey()
+
     function Page() {
-      const { status } = useQuery('test', async () => {
+      const { status } = useQuery(key, async () => {
         await sleep(10)
         return 'test'
       })
@@ -288,27 +296,31 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/147
   it('should not pass stringified variables to query function', async () => {
+    const key = queryKey()
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => sleep(10))
 
     const variables = { number: 5, boolean: false, object: {}, array: [] }
 
     function Page() {
-      useQuery(['test', variables], queryFn)
+      useQuery([key, variables], queryFn)
 
       return null
     }
 
     render(<Page />)
 
-    expect(queryFn).toHaveBeenCalledWith('test', variables)
+    expect(queryFn).toHaveBeenCalledWith(key, variables)
   })
 
   it('should not refetch query on focus when `enabled` is set to `false`', async () => {
+    const key = queryKey()
     const queryFn = jest.fn()
 
     function Page() {
-      const { data = 'default' } = useQuery('test', queryFn, { enabled: false })
+      const { data = 'default' } = useQuery(key, queryFn, {
+        enabled: false,
+      })
 
       return (
         <div>
@@ -329,12 +341,13 @@ describe('useQuery', () => {
   })
 
   it('should set status to error if queryFn throws', async () => {
+    const key = queryKey()
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
     function Page() {
       const { status, error } = useQuery<undefined, string, string>(
-        'test',
+        key,
         () => {
           return Promise.reject('Error test jaylen')
         },
@@ -358,6 +371,7 @@ describe('useQuery', () => {
   })
 
   it('should retry specified number of times', async () => {
+    const key = queryKey()
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
@@ -367,7 +381,7 @@ describe('useQuery', () => {
     })
 
     function Page() {
-      const { status, failureCount } = useQuery('test', queryFn, {
+      const { status, failureCount } = useQuery(key, queryFn, {
         retry: 1,
         retryDelay: 1,
       })
@@ -393,6 +407,8 @@ describe('useQuery', () => {
   })
 
   it('should not retry if retry function `false`', async () => {
+    const key = queryKey()
+
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
@@ -411,7 +427,7 @@ describe('useQuery', () => {
         undefined,
         string,
         string
-      >('test', queryFn, {
+      >(key, queryFn, {
         retryDelay: 1,
         retry: (_failureCount, error) => error !== 'NoRetry',
       })
@@ -438,15 +454,14 @@ describe('useQuery', () => {
   })
 
   it('should garbage collect queries without data immediately', async () => {
+    const key = queryKey()
+
     function Page() {
       const [filter, setFilter] = React.useState('')
-      const { data } = useQuery(
-        ['todos', { filter }],
-        async (_key, { filter }) => {
-          await sleep(10)
-          return `todo ${filter}`
-        }
-      )
+      const { data } = useQuery([key, { filter }], async (_key, { filter }) => {
+        await sleep(10)
+        return `todo ${filter}`
+      })
 
       return (
         <div>
@@ -465,15 +480,17 @@ describe('useQuery', () => {
     fireEvent.click(rendered.getByText('update'))
     fireEvent.click(rendered.getByText('update'))
 
-    expect(Object.keys(queryCache.queries).length).toEqual(5)
+    expect(queryCache.getQuery([key, { filter: 'a' }])).not.toBeUndefined()
 
     await waitFor(() => rendered.getByText('todo aaaa'))
 
-    expect(Object.keys(queryCache.queries).length).toEqual(1)
+    expect(queryCache.getQuery([key, { filter: 'a' }])).toBeUndefined()
   })
 
   // See https://github.com/tannerlinsley/react-query/issues/160
   it('should continue retry after focus regain', async () => {
+    const key = queryKey()
+
     const originalVisibilityState = document.visibilityState
 
     function mockVisibilityState(value: string) {
@@ -487,7 +504,7 @@ describe('useQuery', () => {
     mockVisibilityState('hidden')
 
     function Page() {
-      const query = useQuery('test', () => Promise.reject('fetching error'), {
+      const query = useQuery(key, () => Promise.reject('fetching error'), {
         retry: 3,
         retryDelay: 1,
       })
@@ -517,6 +534,8 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/195
   it('should refetch if stale after a prefetch', async () => {
+    const key = queryKey()
+
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => sleep(10).then(() => 'data'))
 
@@ -524,7 +543,7 @@ describe('useQuery', () => {
     prefetchQueryFn.mockImplementation(() => sleep(16).then(() => 'not yet...'))
 
     await act(() =>
-      queryCache.prefetchQuery('test', prefetchQueryFn, {
+      queryCache.prefetchQuery(key, prefetchQueryFn, {
         staleTime: 10,
       })
     )
@@ -532,7 +551,7 @@ describe('useQuery', () => {
     await act(() => sleep(20))
 
     function Page() {
-      const query = useQuery('test', queryFn)
+      const query = useQuery(key, queryFn)
 
       return (
         <div>
@@ -550,6 +569,8 @@ describe('useQuery', () => {
   })
 
   it('should not refetch if not stale after a prefetch', async () => {
+    const key = queryKey()
+
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => sleep(10).then(() => 'data'))
 
@@ -557,13 +578,13 @@ describe('useQuery', () => {
     prefetchQueryFn.mockImplementation(() => sleep(16).then(() => 'not yet...'))
 
     await act(() =>
-      queryCache.prefetchQuery('test', prefetchQueryFn, {
-        staleTime: 10,
+      queryCache.prefetchQuery(key, prefetchQueryFn, {
+        staleTime: 1000,
       })
     )
 
     function Page() {
-      const query = useQuery('test', queryFn)
+      const query = useQuery(key, queryFn)
 
       return (
         <div>
@@ -582,6 +603,8 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/190
   it('should reset failureCount on successful fetch', async () => {
+    const key = queryKey()
+
     const consoleMock = jest.spyOn(console, 'error')
     consoleMock.mockImplementation(() => undefined)
 
@@ -589,7 +612,7 @@ describe('useQuery', () => {
       let counter = 0
 
       const query = useQuery(
-        'test',
+        key,
         async () => {
           if (counter < 2) {
             counter++
@@ -618,17 +641,19 @@ describe('useQuery', () => {
 
   // See https://github.com/tannerlinsley/react-query/issues/199
   it('should use prefetched data for dependent query', async () => {
+    const key = queryKey()
+
     function Page() {
       const [enabled, setEnabled] = React.useState(false)
       const [isPrefetched, setPrefetched] = React.useState(false)
 
-      const query = useQuery('key', () => undefined, {
+      const query = useQuery(key, () => undefined, {
         enabled,
       })
 
       React.useEffect(() => {
         async function prefetch() {
-          await queryCache.prefetchQuery('key', () =>
+          await queryCache.prefetchQuery(key, () =>
             Promise.resolve('prefetched data')
           )
           setPrefetched(true)
@@ -653,10 +678,12 @@ describe('useQuery', () => {
   })
 
   it('should support dependent queries via the enable config option', async () => {
+    const key = queryKey()
+
     function Page() {
       const [shouldFetch, setShouldFetch] = React.useState(false)
 
-      const query = useQuery('key', () => 'data', {
+      const query = useQuery(key, () => 'data', {
         enabled: shouldFetch,
       })
 
@@ -686,8 +713,12 @@ describe('useQuery', () => {
   })
 
   it('should not mark query as fetching, when using initialData', async () => {
+    const key = queryKey()
+
     function Page() {
-      const query = useQuery('key', () => 'serverData', { initialData: 'data' })
+      const query = useQuery(key, () => 'serverData', {
+        initialData: 'data',
+      })
 
       return (
         <div>
@@ -704,8 +735,10 @@ describe('useQuery', () => {
   })
 
   it('should initialize state properly, when initialData is falsy', async () => {
+    const key = queryKey()
+
     function Page() {
-      const query = useQuery('key', () => 1, { initialData: 0 })
+      const query = useQuery(key, () => 1, { initialData: 0 })
 
       return (
         <div>
@@ -725,11 +758,12 @@ describe('useQuery', () => {
 
   // // See https://github.com/tannerlinsley/react-query/issues/214
   it('data should persist when enabled is changed to false', async () => {
+    const key = queryKey()
     const callback = jest.fn()
 
     function Page() {
       const [shouldFetch, setShouldFetch] = React.useState(true)
-      const query = useQuery('key', () => 'fetched data', {
+      const query = useQuery(key, () => 'fetched data', {
         enabled: shouldFetch,
         initialData: shouldFetch ? 'initial' : 'initial falsy',
       })
@@ -759,12 +793,13 @@ describe('useQuery', () => {
   })
 
   it('it should support enabled:false in query object syntax', async () => {
+    const key = queryKey()
     const queryFn = jest.fn()
     queryFn.mockImplementation(() => 'data')
 
     function Page() {
       const { status } = useQuery({
-        queryKey: 'key',
+        queryKey: key,
         queryFn,
         config: {
           enabled: false,
@@ -775,16 +810,17 @@ describe('useQuery', () => {
 
     const rendered = render(<Page />)
 
-    const cachedQueries = Object.keys(queryCache.queries).length
     expect(queryFn).not.toHaveBeenCalled()
-    expect(cachedQueries).toEqual(1)
+    expect(queryCache.getQuery(key)).not.toBeUndefined()
     rendered.getByText('status: idle')
   })
 
   // See https://github.com/tannerlinsley/react-query/issues/360
   test('should init to status:idle when enabled is falsey', async () => {
+    const key = queryKey()
+
     function Page() {
-      const query = useQuery('key', () => undefined, {
+      const query = useQuery(key, () => undefined, {
         enabled: false,
       })
 
@@ -801,8 +837,10 @@ describe('useQuery', () => {
   })
 
   test('should not schedule garbage collection, if cacheTimeout is set to `Infinity`', async () => {
+    const key = queryKey()
+
     function Page() {
-      const query = useQuery('test', () => 'fetched data', {
+      const query = useQuery(key, () => 'fetched data', {
         cacheTime: Infinity,
       })
       return <div>{query.data}</div>
@@ -814,17 +852,18 @@ describe('useQuery', () => {
 
     rendered.unmount()
 
-    const query = queryCache.getQuery('test')
+    const query = queryCache.getQuery(key)
     // @ts-expect-error
     expect(query!.cacheTimeout).toBe(undefined)
   })
 
   it('should not cause memo churn when data does not change', async () => {
+    const key = queryKey()
     const queryFn = jest.fn()
     const memoFn = jest.fn()
 
     function Page() {
-      const result = useQuery('test', async () => {
+      const result = useQuery(key, async () => {
         await sleep(10)
         return (
           queryFn() || {
@@ -861,11 +900,12 @@ describe('useQuery', () => {
   })
 
   it('should update data upon interval changes', async () => {
+    const key = queryKey()
     let count = 0
 
     function Page() {
       const [int, setInt] = React.useState(200)
-      const { data } = useQuery('/api', () => count++, {
+      const { data } = useQuery(key, () => count++, {
         refetchInterval: int,
       })
 
diff --git a/src/react/tests/utils.tsx b/src/react/tests/utils.tsx
index 7aa167431..ce5380f9c 100644
--- a/src/react/tests/utils.tsx
+++ b/src/react/tests/utils.tsx
@@ -1,3 +1,10 @@
+let queryKeyCount = 0
+
+export function queryKey(): string {
+  queryKeyCount++
+  return `query_${queryKeyCount}`
+}
+
 export function sleep(timeout: number): Promise<void> {
   return new Promise((resolve, _reject) => {
     setTimeout(resolve, timeout)