From cbec5d7cead2b639db2cf883b888f6927e795d86 Mon Sep 17 00:00:00 2001 From: Niek Date: Sat, 6 Feb 2021 10:13:27 +0100 Subject: [PATCH 1/7] feat: add suspense to useQueries --- src/core/queriesObserver.ts | 20 +++- src/react/tests/useQueries.test.tsx | 167 +++++++++++++++++++++++++++- src/react/useQueries.ts | 99 +++++++++++++++-- 3 files changed, 275 insertions(+), 11 deletions(-) diff --git a/src/core/queriesObserver.ts b/src/core/queriesObserver.ts index 39c3a05ee6..98a456e931 100644 --- a/src/core/queriesObserver.ts +++ b/src/core/queriesObserver.ts @@ -1,12 +1,20 @@ import { difference, getQueryKeyHashFn, replaceAt } from './utils' import { notifyManager } from './notifyManager' -import type { QueryObserverOptions, QueryObserverResult } from './types' +import type { + QueryObserverOptions, + QueryObserverResult, + RefetchOptions, +} from './types' import type { QueryClient } from './queryClient' import { QueryObserver } from './queryObserver' import { Subscribable } from './subscribable' type QueriesObserverListener = (result: QueryObserverResult[]) => void +interface QueriesRefetchOptions extends RefetchOptions { + filter: (observer: QueryObserver) => boolean +} + export class QueriesObserver extends Subscribable { private client: QueryClient private result: QueryObserverResult[] @@ -57,6 +65,16 @@ export class QueriesObserver extends Subscribable { return this.result } + refetch(options?: QueriesRefetchOptions): Promise { + let observers = this.observers + + if (options?.filter) { + observers = this.observers.filter(options.filter) + } + + return Promise.all(observers.map(observer => observer.refetch(options))) + } + private updateObservers(): void { let hasIndexChange = false diff --git a/src/react/tests/useQueries.test.tsx b/src/react/tests/useQueries.test.tsx index 2a0e6f8257..2cc8947f4c 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/react/tests/useQueries.test.tsx @@ -1,7 +1,15 @@ +import { fireEvent, waitFor } from '@testing-library/react' import React from 'react' +import { ErrorBoundary } from 'react-error-boundary' -import { queryKey, renderWithClient, sleep } from './utils' -import { useQueries, QueryClient, UseQueryResult, QueryCache } from '../..' +import { mockConsoleError, queryKey, renderWithClient, sleep } from './utils' +import { + useQueries, + QueryClient, + UseQueryResult, + QueryCache, + useQueryErrorResetBoundary, +} from '../..' describe('useQueries', () => { const queryCache = new QueryCache() @@ -30,4 +38,159 @@ describe('useQueries', () => { expect(results[1]).toMatchObject([{ data: 1 }, { data: undefined }]) expect(results[2]).toMatchObject([{ data: 1 }, { data: 2 }]) }) + + it('should render the correct amount of times in suspense mode', async () => { + const key1 = queryKey() + const key2 = queryKey() + const results: UseQueryResult[][] = [] + + let renders = 0 + let count1 = 10 + let count2 = 20 + + function Page() { + renders++ + + const [stateKey1, setStateKey1] = React.useState(key1) + + const result = useQueries([ + { + queryKey: stateKey1, + queryFn: async () => { + count1++ + await sleep(10) + return count1 + }, + }, + { + queryKey: key2, + queryFn: async () => { + count2++ + await sleep(10) + return count2 + }, + suspense: true, + }, + ]) + + results.push(result) + + return ( + + + )} + > + + + + + ) + } + + const rendered = renderWithClient(queryClient, ) + + await waitFor(() => rendered.getByText('Loading...')) + await waitFor(() => rendered.getByText('error boundary')) + await waitFor(() => rendered.getByText('retry')) + fireEvent.click(rendered.getByText('retry')) + await waitFor(() => rendered.getByText('error boundary')) + await waitFor(() => rendered.getByText('retry')) + succeed = true + fireEvent.click(rendered.getByText('retry')) + await waitFor(() => rendered.getByText('data1: data1')) + await waitFor(() => rendered.getByText('data2: data2')) + + consoleMock.mockRestore() + }) }) diff --git a/src/react/useQueries.ts b/src/react/useQueries.ts index 81774b9ae8..c2b88329aa 100644 --- a/src/react/useQueries.ts +++ b/src/react/useQueries.ts @@ -3,31 +3,114 @@ import React from 'react' import { notifyManager } from '../core/notifyManager' import { QueriesObserver } from '../core/queriesObserver' import { useQueryClient } from './QueryClientProvider' +import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary' import { UseQueryOptions, UseQueryResult } from './types' -export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { +export function useQueries( + queries: UseQueryOptions[] +): UseQueryResult[] { const queryClient = useQueryClient() + const errorResetBoundary = useQueryErrorResetBoundary() + const defaultedQueries: UseQueryOptions[] = [] + + let someSuspense = false + let someUseErrorBoundary = false + + queries.forEach(options => { + const defaulted = queryClient.defaultQueryObserverOptions(options) + + // Include callbacks in batch renders + if (defaulted.onError) { + defaulted.onError = notifyManager.batchCalls(defaulted.onError) + } + + if (defaulted.onSuccess) { + defaulted.onSuccess = notifyManager.batchCalls(defaulted.onSuccess) + } + + if (defaulted.onSettled) { + defaulted.onSettled = notifyManager.batchCalls(defaulted.onSettled) + } + + if (defaulted.suspense) { + someSuspense = true + } + + if (defaulted.useErrorBoundary) { + someUseErrorBoundary = true + } + + defaultedQueries.push(defaulted) + }) + + if (someSuspense) { + defaultedQueries.forEach(options => { + // Always set stale time when using suspense to prevent + // fetching again when directly re-mounting after suspense + if (typeof options.staleTime !== 'number') { + options.staleTime = 1000 + } + + // Prevent retrying failed query if the error boundary has not been reset yet + if (!errorResetBoundary.isReset()) { + options.retryOnMount = false + } + }) + } // Create queries observer const observerRef = React.useRef() const observer = - observerRef.current || new QueriesObserver(queryClient, queries) + observerRef.current || new QueriesObserver(queryClient, defaultedQueries) observerRef.current = observer // Update queries if (observer.hasListeners()) { - observer.setQueries(queries) + observer.setQueries(defaultedQueries) } const [currentResult, setCurrentResult] = React.useState(() => observer.getCurrentResult() ) + let someError + let someIsLoading = false + let someIsError = false + + currentResult.forEach(result => { + if (result.isLoading) { + someIsLoading = true + } + + if (result.isError) { + someIsError = true + } + + if (result.error) { + someError = result.error + } + }) + // Subscribe to the observer - React.useEffect( - () => observer.subscribe(notifyManager.batchCalls(setCurrentResult)), - [observer] - ) + React.useEffect(() => { + errorResetBoundary.clearReset() + return observer.subscribe(notifyManager.batchCalls(setCurrentResult)) + }, [observer, errorResetBoundary]) + + // Handle suspense + if (someSuspense || someUseErrorBoundary) { + if (someSuspense && someIsLoading) { + errorResetBoundary.clearReset() + const unsubscribe = observer.subscribe() + throw observer + .refetch({ filter: x => x.getCurrentResult().isLoading }) + .finally(unsubscribe) + } + + if (someIsError) { + throw someError + } + } - return currentResult + return currentResult as UseQueryResult[] } From ed20430dbe4b3b2ce52ed6a9c9bcb81b502e884d Mon Sep 17 00:00:00 2001 From: Stephen Sachs Date: Sun, 11 Apr 2021 17:59:45 -0400 Subject: [PATCH 2/7] Format changes --- src/core/tests/queryClient.test.tsx | 40 +++++++++++++++++------------ src/react/tests/useQueries.test.tsx | 40 +++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/core/tests/queryClient.test.tsx b/src/core/tests/queryClient.test.tsx index ac17879339..69afd7f56c 100644 --- a/src/core/tests/queryClient.test.tsx +++ b/src/core/tests/queryClient.test.tsx @@ -191,14 +191,13 @@ describe('queryClient', () => { type StrictQueryKey = ['strict', string] const key: StrictQueryKey = ['strict', queryKey()] - const fetchFn: QueryFunction = () => ( + const fetchFn: QueryFunction = () => Promise.resolve('data') - ) await expect( queryClient.fetchQuery( key, - fetchFn, + fetchFn ) ).resolves.toEqual('data') }) @@ -309,15 +308,16 @@ describe('queryClient', () => { pageParams: [undefined], } - const fetchFn: QueryFunction = () => ( + const fetchFn: QueryFunction = () => Promise.resolve(data.pages[0]) - ) await expect( - queryClient.fetchInfiniteQuery( - key, - fetchFn, - ) + queryClient.fetchInfiniteQuery< + StrictData, + any, + StrictData, + StrictQueryKey + >(key, fetchFn) ).resolves.toEqual(data) }) @@ -345,11 +345,15 @@ describe('queryClient', () => { type StrictQueryKey = ['strict', string] const key: StrictQueryKey = ['strict', queryKey()] - const fetchFn: QueryFunction = () => ( + const fetchFn: QueryFunction = () => Promise.resolve('data') - ) - await queryClient.prefetchInfiniteQuery(key, fetchFn) + await queryClient.prefetchInfiniteQuery< + StrictData, + any, + StrictData, + StrictQueryKey + >(key, fetchFn) const result = queryClient.getQueryData(key) @@ -381,13 +385,17 @@ describe('queryClient', () => { type StrictQueryKey = ['strict', string] const key: StrictQueryKey = ['strict', queryKey()] - const fetchFn: QueryFunction = () => ( + const fetchFn: QueryFunction = () => Promise.resolve('data') - ) - await queryClient.prefetchQuery(key, fetchFn); + await queryClient.prefetchQuery< + StrictData, + any, + StrictData, + StrictQueryKey + >(key, fetchFn) - const result = queryClient.getQueryData(key); + const result = queryClient.getQueryData(key) expect(result).toEqual('data') }) diff --git a/src/react/tests/useQueries.test.tsx b/src/react/tests/useQueries.test.tsx index 1ee82861df..d612225602 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/react/tests/useQueries.test.tsx @@ -22,8 +22,20 @@ describe('useQueries', () => { function Page() { const result = useQueries([ - { queryKey: key1, queryFn: async () => { await sleep(5); return 1; } }, - { queryKey: key2, queryFn: async () => { await sleep(7); return 2; } }, + { + queryKey: key1, + queryFn: async () => { + await sleep(5) + return 1 + }, + }, + { + queryKey: key2, + queryFn: async () => { + await sleep(7) + return 2 + }, + }, ]) results.push(result) return null @@ -46,14 +58,32 @@ describe('useQueries', () => { function Page() { const result = useQueries([ - { queryKey: key1, queryFn: async () => { await sleep(5); return 1; }, suspense: true }, - { queryKey: key2, queryFn: async () => { await sleep(7); return 2; } }, + { + queryKey: key1, + queryFn: async () => { + await sleep(5) + return 1 + }, + suspense: true, + }, + { + queryKey: key2, + queryFn: async () => { + await sleep(7) + return 2 + }, + }, ]) results.push(result) return null } - renderWithClient(queryClient, ) + renderWithClient( + queryClient, + + + + ) await sleep(10) From b7dea5316a8af9f7967922555a284ad417e23953 Mon Sep 17 00:00:00 2001 From: Stephen Sachs Date: Mon, 12 Apr 2021 18:15:06 -0400 Subject: [PATCH 3/7] fix: Remove console.log from useQueries.ts --- src/react/useQueries.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/react/useQueries.ts b/src/react/useQueries.ts index 5711e6f559..aba6bcf8e2 100644 --- a/src/react/useQueries.ts +++ b/src/react/useQueries.ts @@ -58,7 +58,6 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { // Handle suspense if (someSuspense || someUseErrorBoundary) { if (someSuspense && someIsLoading) { - console.log('suspense') errorResetBoundary.clearReset() const unsubscribe = obsRef.current.subscribe() throw obsRef From 84e5eb9ae38eec17c819a2df55bbec5d1e6fdc62 Mon Sep 17 00:00:00 2001 From: Steev Sachs Date: Tue, 29 Jun 2021 11:23:15 -0400 Subject: [PATCH 4/7] Clean up post-merge and improve test coverage --- src/core/queriesObserver.ts | 4 +- src/react/tests/useQueries.test.tsx | 29 ++++++++---- src/react/useQueries.ts | 73 +++++++++++++++++------------ 3 files changed, 65 insertions(+), 41 deletions(-) diff --git a/src/core/queriesObserver.ts b/src/core/queriesObserver.ts index 3f4d56f083..778e0f05b7 100644 --- a/src/core/queriesObserver.ts +++ b/src/core/queriesObserver.ts @@ -1,7 +1,9 @@ import { difference, replaceAt } from './utils' import { notifyManager } from './notifyManager' import type { - QueryObserverOptions, QueryObserverResult, RefetchOptions + QueryObserverOptions, + QueryObserverResult, + RefetchOptions, } from './types' import type { QueryClient } from './queryClient' import { NotifyOptions, QueryObserver } from './queryObserver' diff --git a/src/react/tests/useQueries.test.tsx b/src/react/tests/useQueries.test.tsx index d612225602..56696b07de 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/react/tests/useQueries.test.tsx @@ -9,6 +9,7 @@ import { UseQueryResult, QueryCache, useQueryErrorResetBoundary, + useQueryClient, } from '../..' describe('useQueries', () => { @@ -54,7 +55,16 @@ describe('useQueries', () => { it('should return the correct states with suspense', async () => { const key1 = queryKey() const key2 = queryKey() - const results: UseQueryResult[][] = [] + const results: any[] = [] + + function Fallback() { + const qc = useQueryClient() + const queryStates = [qc.getQueryState(key1), qc.getQueryState(key2)] + + results.push(queryStates) + + return null + } function Page() { const result = useQueries([ @@ -80,15 +90,16 @@ describe('useQueries', () => { renderWithClient( queryClient, - + }> ) - await sleep(10) - - expect(results.length).toBe(1) - expect(results[0]).toMatchObject([{ data: 1 }, { data: 2 }]) + await waitFor(() => { + expect(results[0]).toMatchObject([{ data: undefined }, { data: undefined }]) + expect(results[1]).toMatchObject([{ data: 1 }, { data: 2 }]) + expect(results.length).toBe(2); + }) }) it('should retry fetch if the reset error boundary has been reset with global hook', async () => { @@ -103,7 +114,7 @@ describe('useQueries', () => { { queryKey: key1, queryFn: async () => { - await sleep(10) + await sleep(5) if (!succeed) { throw new Error('Suspense Error Bingo') } else { @@ -122,8 +133,8 @@ describe('useQueries', () => { return (
-
data1: {results[0].data}
-
data2: {results[1].data}
+
data1: {results[0]?.data}
+
data2: {results[1]?.data}
) } diff --git a/src/react/useQueries.ts b/src/react/useQueries.ts index c18338a05e..0ae3eb12cd 100644 --- a/src/react/useQueries.ts +++ b/src/react/useQueries.ts @@ -5,29 +5,47 @@ import { QueriesObserver } from '../core/queriesObserver' import { useQueryClient } from './QueryClientProvider' import { useQueryErrorResetBoundary } from './QueryErrorResetBoundary' import { UseQueryOptions, UseQueryResult } from './types' -import { initDefaultedOptions } from './useBaseQuery' export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { const mountedRef = React.useRef(false) const [, forceUpdate] = React.useState(0) const queryClient = useQueryClient() - const errorResetBoundary = useQueryErrorResetBoundary(); + const errorResetBoundary = useQueryErrorResetBoundary() - const defaultedQueries = queries.map((options) => initDefaultedOptions({ errorResetBoundary, options, queryClient })) + const defaultedQueries = queries.map(options => { + const defaultedOptions = queryClient.defaultQueryObserverOptions(options) - const [observer] = React.useState( - () => new QueriesObserver(queryClient, defaultedQueries) + // Make sure the results are already in fetching state before subscribing or updating options + defaultedOptions.optimisticResults = true + + return defaultedOptions + }) + + const observerRef = React.useRef( + new QueriesObserver(queryClient, defaultedQueries) ) - const result = observer.getOptimisticResult(defaultedQueries) + const result = observerRef.current.getOptimisticResult(defaultedQueries) + + React.useEffect(() => { + // Do not notify on updates because of changes in the options because + // these changes should already be reflected in the optimistic result. + observerRef.current.setQueries(defaultedQueries, { listeners: false }) + }, [defaultedQueries, observerRef.current]) + + const someSuspense = defaultedQueries.some(q => q.suspense) + const someUseErrorBoundary = defaultedQueries.some(q => q.useErrorBoundary) + const firstResultWithError = result.find(r => r.error) + const someError = firstResultWithError?.error + const someIsLoading = result.some(r => r.isLoading) React.useEffect(() => { mountedRef.current = true - const unsubscribe = observer.subscribe( + const unsubscribe = observerRef.current.subscribe( notifyManager.batchCalls(() => { - if (mountedRef.current) { + if (mountedRef.current && someIsLoading) { forceUpdate(x => x + 1) } }) @@ -37,34 +55,27 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { mountedRef.current = false unsubscribe() } - }, [observer]) - - React.useEffect(() => { - // Do not notify on updates because of changes in the options because - // these changes should already be reflected in the optimistic result. - observer.setQueries(defaultedQueries, { listeners: false }) - }, [defaultedQueries, observer]) - + }, [observerRef.current]) - const someSuspense = defaultedQueries.some((q) => q.suspense); - const someUseErrorBoundary = defaultedQueries.some((q) => q.useErrorBoundary); - const firstResultWithError = result.find((r) => r.isError); - const someError = firstResultWithError?.error; - const someIsError = !!firstResultWithError; - const someIsLoading = result.some((r) => r.isLoading); + const handleReset = React.useCallback(() => { + errorResetBoundary.clearReset() + const unsubscribe = observerRef.current.subscribe() + throw observerRef.current + .refetch({ filter: x => x.getCurrentResult().isLoading }) + .finally(unsubscribe) + }, [errorResetBoundary, observerRef.current]) - // Handle suspense + // Handle suspense and error boundaries if (someSuspense || someUseErrorBoundary) { - if (someSuspense && someIsLoading) { - errorResetBoundary.clearReset() - const unsubscribe = obsRef.current.subscribe() - throw obsRef - .current.refetch({ filter: x => x.getCurrentResult().isLoading }) - .finally(unsubscribe) + if (someError) { + if (errorResetBoundary.isReset()) { + handleReset(); + } + throw someError } - if (someIsError) { - throw someError + if (someSuspense && someIsLoading) { + handleReset(); } } From 9a7f633cf931933d1a8aea58445137615b3bf804 Mon Sep 17 00:00:00 2001 From: Steev Sachs Date: Tue, 29 Jun 2021 12:53:38 -0400 Subject: [PATCH 5/7] Fixes in response to checks * Run yarn format * Fix bad dependencies in useQueries * Fix explicit any in useQueries.test --- src/react/tests/useQueries.test.tsx | 12 ++++++++---- src/react/useQueries.ts | 12 ++++++------ 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/react/tests/useQueries.test.tsx b/src/react/tests/useQueries.test.tsx index 56696b07de..f960a767ad 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/react/tests/useQueries.test.tsx @@ -11,6 +11,7 @@ import { useQueryErrorResetBoundary, useQueryClient, } from '../..' +import { QueryState } from '../../core/query' describe('useQueries', () => { const queryCache = new QueryCache() @@ -55,11 +56,11 @@ describe('useQueries', () => { it('should return the correct states with suspense', async () => { const key1 = queryKey() const key2 = queryKey() - const results: any[] = [] + const results: (QueryState[] | UseQueryResult[])[] = [] function Fallback() { const qc = useQueryClient() - const queryStates = [qc.getQueryState(key1), qc.getQueryState(key2)] + const queryStates = [qc.getQueryState(key1)!, qc.getQueryState(key2)!] results.push(queryStates) @@ -96,9 +97,12 @@ describe('useQueries', () => { ) await waitFor(() => { - expect(results[0]).toMatchObject([{ data: undefined }, { data: undefined }]) + expect(results[0]).toMatchObject([ + { data: undefined }, + { data: undefined }, + ]) expect(results[1]).toMatchObject([{ data: 1 }, { data: 2 }]) - expect(results.length).toBe(2); + expect(results.length).toBe(2) }) }) diff --git a/src/react/useQueries.ts b/src/react/useQueries.ts index 0ae3eb12cd..e18d36e666 100644 --- a/src/react/useQueries.ts +++ b/src/react/useQueries.ts @@ -32,7 +32,7 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { // Do not notify on updates because of changes in the options because // these changes should already be reflected in the optimistic result. observerRef.current.setQueries(defaultedQueries, { listeners: false }) - }, [defaultedQueries, observerRef.current]) + }, [defaultedQueries]) const someSuspense = defaultedQueries.some(q => q.suspense) const someUseErrorBoundary = defaultedQueries.some(q => q.useErrorBoundary) @@ -45,7 +45,7 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { const unsubscribe = observerRef.current.subscribe( notifyManager.batchCalls(() => { - if (mountedRef.current && someIsLoading) { + if (mountedRef.current && someIsLoading) { forceUpdate(x => x + 1) } }) @@ -55,7 +55,7 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { mountedRef.current = false unsubscribe() } - }, [observerRef.current]) + }, [someIsLoading]) const handleReset = React.useCallback(() => { errorResetBoundary.clearReset() @@ -63,19 +63,19 @@ export function useQueries(queries: UseQueryOptions[]): UseQueryResult[] { throw observerRef.current .refetch({ filter: x => x.getCurrentResult().isLoading }) .finally(unsubscribe) - }, [errorResetBoundary, observerRef.current]) + }, [errorResetBoundary]) // Handle suspense and error boundaries if (someSuspense || someUseErrorBoundary) { if (someError) { if (errorResetBoundary.isReset()) { - handleReset(); + handleReset() } throw someError } if (someSuspense && someIsLoading) { - handleReset(); + handleReset() } } From 9053f5dc041f7940477c09b130d5a46d55f3e319 Mon Sep 17 00:00:00 2001 From: Steev Sachs Date: Sun, 29 Aug 2021 09:02:34 -0400 Subject: [PATCH 6/7] Fix formatting after merge --- src/react/tests/useQueries.test.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/react/tests/useQueries.test.tsx b/src/react/tests/useQueries.test.tsx index 0d35e407e8..d5f4192200 100644 --- a/src/react/tests/useQueries.test.tsx +++ b/src/react/tests/useQueries.test.tsx @@ -2,7 +2,13 @@ import { fireEvent, waitFor } from '@testing-library/react' import React from 'react' import { ErrorBoundary } from 'react-error-boundary' -import { mockConsoleError, queryKey, renderWithClient, setActTimeout, sleep } from './utils' +import { + mockConsoleError, + queryKey, + renderWithClient, + setActTimeout, + sleep, +} from './utils' import { useQueries, QueryClient, From e278a7cddd5159b49c4037f654d02eac1ca7666d Mon Sep 17 00:00:00 2001 From: Steev Sachs Date: Sun, 29 Aug 2021 09:07:55 -0400 Subject: [PATCH 7/7] Update documentation to describe the summary behavior of suspense and useErrorBoundary in useQueries --- docs/src/pages/reference/useQueries.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/src/pages/reference/useQueries.md b/docs/src/pages/reference/useQueries.md index f69e5fe3af..04622436f8 100644 --- a/docs/src/pages/reference/useQueries.md +++ b/docs/src/pages/reference/useQueries.md @@ -12,6 +12,8 @@ const results = useQueries([ ]) ``` +Note that if _any_ query in the array of query option objects is configured with `suspense: true` or `useQueryBoundary: true`, that configuration will apply to _all_ queries handled by that `useQueries` hook. + **Options** The `useQueries` hook accepts an array with query option objects identical to the [`useQuery` hook](/reference/useQuery).