Repository: tannerlinsley/react-query
Pull request #1799 (2021-02-14 08:49:15)

fix: string and array query keys should be similar

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 below or on the icon next to each commit.

🔍 Inspect: [https://vercel.com/tannerlinsley/react-query/k09mxv77r](https://vercel.com/tannerlinsley/react-query/k09mxv77r)  
✅ Preview: [https://react-query-git-feature-stable-query-key.tannerlinsley.vercel.app](https://react-query-git-feature-stable-query-key.tannerlinsley.vercel.app)



Patch:
diff --git a/src/core/queriesObserver.ts b/src/core/queriesObserver.ts
index b18b6d275..0490cde09 100644
--- a/src/core/queriesObserver.ts
+++ b/src/core/queriesObserver.ts
@@ -1,4 +1,4 @@
-import { difference, getQueryKeyHashFn, replaceAt } from './utils'
+import { difference, replaceAt } from './utils'
 import { notifyManager } from './notifyManager'
 import type { QueryObserverOptions, QueryObserverResult } from './types'
 import type { QueryClient } from './queryClient'
@@ -63,8 +63,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
       let observer: QueryObserver | undefined = this.observers[i]
 
       const defaultedOptions = this.client.defaultQueryObserverOptions(options)
-      const hashFn = getQueryKeyHashFn(defaultedOptions)
-      defaultedOptions.queryHash = hashFn(defaultedOptions.queryKey!)
 
       if (
         !observer ||
@@ -94,8 +92,6 @@ export class QueriesObserver extends Subscribable<QueriesObserverListener> {
         const defaultedOptions = this.client.defaultQueryObserverOptions(
           options
         )
-        const hashFn = getQueryKeyHashFn(defaultedOptions)
-        defaultedOptions.queryHash = hashFn(defaultedOptions.queryKey!)
 
         if (
           !observer ||
diff --git a/src/core/queryCache.ts b/src/core/queryCache.ts
index a0652fe6d..293976fe2 100644
--- a/src/core/queryCache.ts
+++ b/src/core/queryCache.ts
@@ -1,6 +1,6 @@
 import {
   QueryFilters,
-  getQueryKeyHashFn,
+  hashQueryKeyByOptions,
   matchQuery,
   parseFilterArgs,
 } from './utils'
@@ -43,7 +43,8 @@ export class QueryCache extends Subscribable<QueryCacheListener> {
     state?: QueryState<TData, TError>
   ): Query<TQueryFnData, TError, TData> {
     const queryKey = options.queryKey!
-    const queryHash = options.queryHash ?? getQueryKeyHashFn(options)(queryKey)
+    const queryHash =
+      options.queryHash ?? hashQueryKeyByOptions(queryKey, options)
     let query = this.get<TQueryFnData, TError, TData>(queryHash)
 
     if (!query) {
diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts
index a855b8064..a7230fbfb 100644
--- a/src/core/queryClient.ts
+++ b/src/core/queryClient.ts
@@ -6,7 +6,7 @@ import {
   parseFilterArgs,
   parseQueryArgs,
   partialMatchKey,
-  getQueryKeyHashFn,
+  hashQueryKeyByOptions,
 } from './utils'
 import type {
   DefaultOptions,
@@ -456,8 +456,9 @@ export class QueryClient {
     } as T
 
     if (!defaultedOptions.queryHash && defaultedOptions.queryKey) {
-      defaultedOptions.queryHash = getQueryKeyHashFn(defaultedOptions)(
-        defaultedOptions.queryKey
+      defaultedOptions.queryHash = hashQueryKeyByOptions(
+        defaultedOptions.queryKey,
+        defaultedOptions
       )
     }
 
diff --git a/src/core/utils.ts b/src/core/utils.ts
index 56dc8ca72..7b7870d07 100644
--- a/src/core/utils.ts
+++ b/src/core/utils.ts
@@ -5,7 +5,6 @@ import type {
   MutationOptions,
   QueryFunction,
   QueryKey,
-  QueryKeyHashFunction,
   QueryOptions,
 } from './types'
 
@@ -153,8 +152,7 @@ export function matchQuery(
 
   if (isQueryKey(queryKey)) {
     if (exact) {
-      const hashFn = getQueryKeyHashFn(query.options)
-      if (query.queryHash !== hashFn(queryKey)) {
+      if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) {
         return false
       }
     } else if (!partialMatchKey(query.queryKey, queryKey)) {
@@ -189,17 +187,20 @@ export function matchQuery(
   return true
 }
 
-export function getQueryKeyHashFn(
+export function hashQueryKeyByOptions(
+  queryKey: QueryKey,
   options?: QueryOptions<any, any>
-): QueryKeyHashFunction {
-  return options?.queryKeyHashFn || hashQueryKey
+): string {
+  const hashFn = options?.queryKeyHashFn || hashQueryKey
+  return hashFn(queryKey)
 }
 
 /**
  * Default query keys hash function.
  */
 export function hashQueryKey(queryKey: QueryKey): string {
-  return stableValueHash(queryKey)
+  const asArray = Array.isArray(queryKey) ? queryKey : [queryKey]
+  return stableValueHash(asArray)
 }
 
 /**


Test patch:
diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx
index 1de12ad59..f34d7a080 100644
--- a/src/core/tests/queryClient.test.tsx
+++ b/src/core/tests/queryClient.test.tsx
@@ -140,6 +140,19 @@ describe('queryClient', () => {
       expect(queryClient.getQueryData(key)).toBe('qux')
     })
 
+    test('should use the same query when using similar string or array query keys', () => {
+      const key = queryKey()
+      queryClient.setQueryData(key, '1')
+      expect(queryClient.getQueryData(key)).toBe('1')
+      expect(queryClient.getQueryData([key])).toBe('1')
+      queryClient.setQueryData([key], '2')
+      expect(queryClient.getQueryData(key)).toBe('2')
+      expect(queryClient.getQueryData([key])).toBe('2')
+      queryClient.setQueryData(key, '1')
+      expect(queryClient.getQueryData(key)).toBe('1')
+      expect(queryClient.getQueryData([key])).toBe('1')
+    })
+
     test('should accept an update function', () => {
       const key = queryKey()