Repository: tannerlinsley/react-query
Pull request #2513 (2021-07-29 14:03:43)

feat (QueryClient): getQueriesData for matching multiple queries
Similar to `setQueriesData`, a "get" version to fuzzily return multiple matching queries.

Based the code off of `setQueriesData` and `getQueryData` - couldn't get typing quite right without the assertion to `TData` in the map. I'm sure there's a better way to go with that.

Hope this at least points in the right direction!

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/7JgQdoaugyCQHejnDYLZRXv8oKNk](https://vercel.com/tannerlinsley/react-query/7JgQdoaugyCQHejnDYLZRXv8oKNk)  
✅ Preview: *In Progress*


codesandbox[bot]: This pull request is automatically built and testable in [CodeSandbox](https://codesandbox.io).

  To see build info of the built libraries, click [here](https://ci.codesandbox.io/status/tannerlinsley/react-query/pr/2513/builds/154145) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 3ff6ee78dec0be8b623df833c08a22621d6c948e:

|Sandbox| Source |
|--|--|
|[tannerlinsley/react-query: basic](https://codesandbox.io/s/tannerlinsleyreact-query-basic-43djf)| Configuration |
|[tannerlinsley/react-query: basic-typescript](https://codesandbox.io/s/tannerlinsleyreact-query-basic-typescript-c4em9)| Configuration |


Patch:
diff --git a/docs/src/pages/reference/QueryClient.md b/docs/src/pages/reference/QueryClient.md
index 7bd77186b..cb2e2175c 100644
--- a/docs/src/pages/reference/QueryClient.md
+++ b/docs/src/pages/reference/QueryClient.md
@@ -23,30 +23,31 @@ await queryClient.prefetchQuery('posts', fetchPosts)
 
 Its available methods are:
 
-- [`fetchQuery`](#queryclientfetchquery)
-- [`fetchInfiniteQuery`](#queryclientfetchinfinitequery)
-- [`prefetchQuery`](#queryclientprefetchquery)
-- [`prefetchInfiniteQuery`](#queryclientprefetchinfinitequery)
-- [`getQueryData`](#queryclientgetquerydata)
-- [`setQueryData`](#queryclientsetquerydata)
-- [`setQueriesData`](#queryclientsetqueriesdata)
-- [`getQueryState`](#queryclientgetquerystate)
-- [`invalidateQueries`](#queryclientinvalidatequeries)
-- [`refetchQueries`](#queryclientrefetchqueries)
-- [`cancelQueries`](#queryclientcancelqueries)
-- [`removeQueries`](#queryclientremovequeries)
-- [`resetQueries`](#queryclientresetqueries)
-- [`isFetching`](#queryclientisfetching)
-- [`isMutating`](#queryclientismutating)
-- [`getDefaultOptions`](#queryclientsetdefaultoptions)
-- [`setDefaultOptions`](#queryclientgetdefaultoptions)
-- [`getQueryDefaults`](#queryclientgetquerydefaults)
-- [`setQueryDefaults`](#queryclientsetquerydefaults)
-- [`getMutationDefaults`](#queryclientgetmutationdefaults)
-- [`setMutationDefaults`](#queryclientsetmutationdefaults)
-- [`getQueryCache`](#queryclientgetquerycache)
-- [`getMutationCache`](#queryclientgetmutationcache)
-- [`clear`](#queryclientclear)
+- [`queryClient.fetchQuery`](#queryclientfetchquery)
+- [`queryClient.fetchInfiniteQuery`](#queryclientfetchinfinitequery)
+- [`queryClient.prefetchQuery`](#queryclientprefetchquery)
+- [`queryClient.prefetchInfiniteQuery`](#queryclientprefetchinfinitequery)
+- [`queryClient.getQueryData`](#queryclientgetquerydata)
+- [`queryClient.getQueriesData`](#queryclientgetqueriesdata)
+- [`queryClient.setQueryData`](#queryclientsetquerydata)
+- [`queryClient.getQueryState`](#queryclientgetquerystate)
+- [`queryClient.setQueriesData`](#queryclientsetqueriesdata)
+- [`queryClient.invalidateQueries`](#queryclientinvalidatequeries)
+- [`queryClient.refetchQueries`](#queryclientrefetchqueries)
+- [`queryClient.cancelQueries`](#queryclientcancelqueries)
+- [`queryClient.removeQueries`](#queryclientremovequeries)
+- [`queryClient.resetQueries`](#queryclientresetqueries)
+- [`queryClient.isFetching`](#queryclientisfetching)
+- [`queryClient.isMutating`](#queryclientismutating)
+- [`queryClient.getDefaultOptions`](#queryclientgetdefaultoptions)
+- [`queryClient.setDefaultOptions`](#queryclientsetdefaultoptions)
+- [`queryClient.getQueryDefaults`](#queryclientgetquerydefaults)
+- [`queryClient.setQueryDefaults`](#queryclientsetquerydefaults)
+- [`queryClient.getMutationDefaults`](#queryclientgetmutationdefaults)
+- [`queryClient.setMutationDefaults`](#queryclientsetmutationdefaults)
+- [`queryClient.getQueryCache`](#queryclientgetquerycache)
+- [`queryClient.getMutationCache`](#queryclientgetmutationcache)
+- [`queryClient.clear`](#queryclientclear)
 
 **Options**
 
@@ -175,6 +176,31 @@ const data = queryClient.getQueryData(queryKey)
 - `data: TData | undefined`
   - The data for the cached query, or `undefined` if the query does not exist.
 
+## `queryClient.getQueriesData`
+
+`getQueriesData` is a synchronous function that can be used to get the cached data of multiple queries. Only queries that match the passed queryKey or queryFilter will be returned. If there are no matching queries, an empty array will be returned.
+
+```js
+const data = queryClient.getQueriesData(queryKey | filters)
+```
+
+**Options**
+
+- `queryKey: QueryKey`: [Query Keys](../guides/query-keys) | `filters: QueryFilters`: [Query Filters](../guides/filters#query-filters)
+  - if a queryKey is passed as the argument, the data with queryKeys fuzzily matching this param will be returned
+  - if a filter is passed, the data with queryKeys matching the filter will be returned
+
+**Returns**
+
+- `[queryKey:QueryKey, data:TData | unknown][]`
+  - An array of tuples for the matched query keys, or `[]` if there are no matches. The tuples are the query key and its associated data.
+  
+**Caveats**
+
+Because the returned data in each tuple can be of varying structures (i.e. using a filter to return "active" queries can return different data types), the `TData` generic defaults to `unknown`. If you provide a more specific type to `TData` it is assumed that you are certain each tuple's data entry is all the same type.
+
+This distinction is more a "convenience" for ts devs that know which structure will be returned.
+
 ## `queryClient.setQueryData`
 
 `setQueryData` is a synchronous function that can be used to immediately update a query's cached data. If the query does not exist, it will be created. **If the query is not utilized by a query hook in the default `cacheTime` of 5 minutes, the query will be garbage collected**.
diff --git a/src/core/queryClient.ts b/src/core/queryClient.ts
index 81cd2f9ea..6724cb5a6 100644
--- a/src/core/queryClient.ts
+++ b/src/core/queryClient.ts
@@ -111,6 +111,19 @@ export class QueryClient {
     return this.queryCache.find<TData>(queryKey, filters)?.state.data
   }
 
+  getQueriesData<TData = unknown>(queryKey: QueryKey): [QueryKey, TData][]
+  getQueriesData<TData = unknown>(filters: QueryFilters): [QueryKey, TData][]
+  getQueriesData<TData = unknown>(
+    queryKeyOrFilters: QueryKey | QueryFilters
+  ): [QueryKey, TData][] {
+    return this.getQueryCache()
+      .findAll(queryKeyOrFilters)
+      .map(({ queryKey, state }) => {
+        const data = state.data as TData
+        return [queryKey, data]
+      })
+  }
+
   setQueryData<TData>(
     queryKey: QueryKey,
     updater: Updater<TData | undefined, TData>,
diff --git a/src/core/utils.ts b/src/core/utils.ts
index 49b310ed5..f31a160e2 100644
--- a/src/core/utils.ts
+++ b/src/core/utils.ts
@@ -166,6 +166,13 @@ export function parseFilterArgs<
     : [arg1 || {}, arg2]) as [TFilters, TOptions]
 }
 
+export function parseMutationFilterArgs(
+  arg1?: QueryKey | MutationFilters,
+  arg2?: MutationFilters
+): MutationFilters | undefined {
+  return isQueryKey(arg1) ? { ...arg2, mutationKey: arg1 } : arg1
+}
+
 export function mapQueryStatusFilter(
   active?: boolean,
   inactive?: boolean
diff --git a/src/react/useIsMutating.ts b/src/react/useIsMutating.ts
index 3f6987be2..3353dfc85 100644
--- a/src/react/useIsMutating.ts
+++ b/src/react/useIsMutating.ts
@@ -1,11 +1,21 @@
 import React from 'react'
 
 import { notifyManager } from '../core/notifyManager'
-import { MutationFilters } from '../core/utils'
+import { QueryKey } from '../core/types'
+import { MutationFilters, parseMutationFilterArgs } from '../core/utils'
 import { useQueryClient } from './QueryClientProvider'
 
-export function useIsMutating(filters?: MutationFilters): number {
+export function useIsMutating(filters?: MutationFilters): number
+export function useIsMutating(
+  queryKey?: QueryKey,
+  filters?: MutationFilters
+): number
+export function useIsMutating(
+  arg1?: QueryKey | MutationFilters,
+  arg2?: MutationFilters
+): number {
   const mountedRef = React.useRef(false)
+  const filters = parseMutationFilterArgs(arg1, arg2)
 
   const queryClient = useQueryClient()
 


Test patch:
diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx
index 86adb2c61..75646a4d4 100644
--- a/src/core/tests/queryClient.test.tsx
+++ b/src/core/tests/queryClient.test.tsx
@@ -223,6 +223,37 @@ describe('queryClient', () => {
     })
   })
 
+  describe('getQueriesData', () => {
+    test('should return the query data for all matched queries', () => {
+      const key1 = queryKey()
+      const key2 = queryKey()
+      queryClient.setQueryData([key1, 1], 1)
+      queryClient.setQueryData([key1, 2], 2)
+      queryClient.setQueryData([key2, 2], 2)
+      expect(queryClient.getQueriesData([key1])).toEqual([
+        [[key1, 1], 1],
+        [[key1, 2], 2],
+      ])
+    })
+
+    test('should return empty array if queries are not found', () => {
+      const key = queryKey()
+      expect(queryClient.getQueriesData(key)).toEqual([])
+    })
+
+    test('should accept query filters', () => {
+      queryClient.setQueryData(['key', 1], 1)
+      queryClient.setQueryData(['key', 2], 2)
+      const query1 = queryCache.find(['key', 1])!
+
+      const result = queryClient.getQueriesData({
+        predicate: query => query === query1,
+      })
+
+      expect(result).toEqual([[['key', 1], 1]])
+    })
+  })
+
   describe('fetchQuery', () => {
     test('should not type-error with strict query key', async () => {
       type StrictData = 'data'
diff --git a/src/react/tests/useIsMutating.test.tsx b/src/react/tests/useIsMutating.test.tsx
index 2b1f5b644..e7f147578 100644
--- a/src/react/tests/useIsMutating.test.tsx
+++ b/src/react/tests/useIsMutating.test.tsx
@@ -45,7 +45,7 @@ describe('useIsMutating', () => {
     const queryClient = new QueryClient()
 
     function IsMutating() {
-      const isMutating = useIsMutating({ mutationKey: 'mutation1' })
+      const isMutating = useIsMutating('mutation1')
       isMutatings.push(isMutating)
       return null
     }