Repository: tannerlinsley/react-query
Pull request #3071 (2021-12-08 05:00:39)

feat(useQueries): v4 api to take an object as input
Update the `useQueries` API to take an object as input. This way we can define top level options that are true for all queries and a list of queries.

```js
useQueries({ 
  queries: [{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }, ... ] 
})
```

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


babycourageous: Let me know if anything seems amiss and I'll make any necessary adjustments for ya!
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/3071/builds/196728) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 757e1ff5f65d63f2f854ee764aa84312077b49cf:

|Sandbox| Source |
|--|--|
|[tannerlinsley/react-query: basic](https://codesandbox.io/s/tannerlinsley-react-query-basic-hzviw)| Configuration |
|[tannerlinsley/react-query: basic-typescript](https://codesandbox.io/s/tannerlinsley-react-query-basic-typescript-qmpr0)| Configuration |

codecov[bot]: # [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) Report
> Merging [#3071](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (757e1ff) into [alpha](https://codecov.io/gh/tannerlinsley/react-query/commit/b84681fdc7d37645cd149f40e98cb18fd8bc393c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) (b84681f) will **not change** coverage.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/tannerlinsley/react-query/pull/3071/graphs/tree.svg?width=650&height=150&src=pr&token=jqEbswkEDQ&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)

```diff
@@           Coverage Diff           @@
##            alpha    #3071   +/-   ##
=======================================
  Coverage   96.80%   96.80%           
=======================================
  Files          47       47           
  Lines        2286     2286           
  Branches      665      665           
=======================================
  Hits         2213     2213           
  Misses         70       70           
  Partials        3        3           
```


| [Impacted Files](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley) | Coverage Δ | |
|---|---|---|
| [src/reactjs/useQueries.ts](https://codecov.io/gh/tannerlinsley/react-query/pull/3071/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley#diff-c3JjL3JlYWN0anMvdXNlUXVlcmllcy50cw==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Last update [b84681f...757e1ff](https://codecov.io/gh/tannerlinsley/react-query/pull/3071?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Tanner+Linsley).

etabackman: I am curious why this is a better params signature than the first param being an array of queries and an optional second param for options which apply to all queries?

That way the simple case where you don't want to provide any options does not require wrapping your param in an object with a `queries` key.

Like this:

```typescript
useQueries(
  [{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }, ... ],
  { ...optionalOptions }
)
```

Patch:
diff --git a/docs/src/pages/guides/does-this-replace-client-state.md b/docs/src/pages/guides/does-this-replace-client-state.md
index 1a907fdf8..f6f5c0c15 100644
--- a/docs/src/pages/guides/does-this-replace-client-state.md
+++ b/docs/src/pages/guides/does-this-replace-client-state.md
@@ -38,7 +38,7 @@ const globalState = {
 }
 ```
 
-This also means that with a few hook calls to `useQuery` and `useMutation`, we also get to remove any boilerplate code that use to manage our server state eg.
+This also means that with a few hook calls to `useQuery` and `useMutation`, we also get to remove any boilerplate code that was used to manage our server state eg.
 
 - Connectors
 - Action Creators
diff --git a/docs/src/pages/guides/migrating-to-react-query-4.md b/docs/src/pages/guides/migrating-to-react-query-4.md
index 14002bec8..d8ee9beb5 100644
--- a/docs/src/pages/guides/migrating-to-react-query-4.md
+++ b/docs/src/pages/guides/migrating-to-react-query-4.md
@@ -207,6 +207,16 @@ new QueryClient({
 })
 ```
 
+### new API for `useQueries`
+
+The `useQueries` hook now accepts an object with a `queries` prop as its input. The value of the `queries` prop is an array of queries (this array is identical to what was passed into `useQueries` in v3).
+
+```diff
+- useQueries([{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }])
++ useQueries({ queries: [{ queryKey1, queryFn1, options1 }, { queryKey2, queryFn2, options2 }] })
+```
+
+
 ### Removed undocumented methods from the `queryClient`
 
 The methods `cancelMutatations` and `executeMutation` were undocumented and unused internally, so we removed them. Since they were just wrappers around methods available on the `mutationCache`, you can still use the functionality.
diff --git a/docs/src/pages/guides/parallel-queries.md b/docs/src/pages/guides/parallel-queries.md
index 79dac5ce8..74b20a701 100644
--- a/docs/src/pages/guides/parallel-queries.md
+++ b/docs/src/pages/guides/parallel-queries.md
@@ -25,17 +25,17 @@ function App () {
 
 If the number of queries you need to execute is changing from render to render, you cannot use manual querying since that would violate the rules of hooks. Instead, React Query provides a `useQueries` hook, which you can use to dynamically execute as many queries in parallel as you'd like.
 
-`useQueries` accepts an **array of query options objects** and returns an **array of query results**:
+`useQueries` accepts an **options object** with a **queries key** whose value is an **array of query objects**. It returns an **array of query results**:
 
 ```js
 function App({ users }) {
-  const userQueries = useQueries(
-    users.map(user => {
+  const userQueries = useQueries({
+    queries: users.map(user => {
       return {
         queryKey: ['user', user.id],
         queryFn: () => fetchUserById(user.id),
       }
     })
-  )
+  })
 }
 ```
diff --git a/docs/src/pages/guides/window-focus-refetching.md b/docs/src/pages/guides/window-focus-refetching.md
index 0058831fe..4e7d0e635 100644
--- a/docs/src/pages/guides/window-focus-refetching.md
+++ b/docs/src/pages/guides/window-focus-refetching.md
@@ -68,10 +68,12 @@ import { AppState } from 'react-native'
 import { focusManager } from 'react-query'
 
 focusManager.setEventListener(handleFocus => {
-  AppState.addEventListener('change', handleFocus)
+  const subscription = AppState.addEventListener('change', state => {
+    handleFocus(state === 'active')
+  })
 
   return () => {
-   AppState.removeEventListener('change', handleFocus)
+    subscription.remove()
   }
 })
 ```
diff --git a/docs/src/pages/reference/useQueries.md b/docs/src/pages/reference/useQueries.md
index f69e5fe3a..0849033ef 100644
--- a/docs/src/pages/reference/useQueries.md
+++ b/docs/src/pages/reference/useQueries.md
@@ -6,15 +6,17 @@ title: useQueries
 The `useQueries` hook can be used to fetch a variable number of queries:
 
 ```js
-const results = useQueries([
-  { queryKey: ['post', 1], queryFn: fetchPost },
-  { queryKey: ['post', 2], queryFn: fetchPost },
-])
+const results = useQueries({
+  queries: [
+    { queryKey: ['post', 1], queryFn: fetchPost },
+    { queryKey: ['post', 2], queryFn: fetchPost }
+  ]
+})
 ```
 
 **Options**
 
-The `useQueries` hook accepts an array with query option objects identical to the [`useQuery` hook](/reference/useQuery).
+The `useQueries` hook accepts an options object with a **queries** key whose value is an array with query option objects identical to the [`useQuery` hook](/reference/useQuery).
 
 **Returns**
 
diff --git a/src/core/infiniteQueryObserver.ts b/src/core/infiniteQueryObserver.ts
index dc0a6a602..063b01ae8 100644
--- a/src/core/infiniteQueryObserver.ts
+++ b/src/core/infiniteQueryObserver.ts
@@ -6,7 +6,11 @@ import type {
   InfiniteQueryObserverResult,
 } from './types'
 import type { QueryClient } from './queryClient'
-import { ObserverFetchOptions, QueryObserver } from './queryObserver'
+import {
+  NotifyOptions,
+  ObserverFetchOptions,
+  QueryObserver,
+} from './queryObserver'
 import {
   hasNextPage,
   hasPreviousPage,
@@ -67,12 +71,16 @@ export class InfiniteQueryObserver<
       TError,
       TData,
       TQueryData
-    >
+    >,
+    notifyOptions?: NotifyOptions
   ): void {
-    super.setOptions({
-      ...options,
-      behavior: infiniteQueryBehavior(),
-    })
+    super.setOptions(
+      {
+        ...options,
+        behavior: infiniteQueryBehavior(),
+      },
+      notifyOptions
+    )
   }
 
   getOptimisticResult(
diff --git a/src/core/mutation.ts b/src/core/mutation.ts
index 093f4107e..04533c11e 100644
--- a/src/core/mutation.ts
+++ b/src/core/mutation.ts
@@ -4,7 +4,7 @@ import type { MutationObserver } from './mutationObserver'
 import { getLogger } from './logger'
 import { notifyManager } from './notifyManager'
 import { Removable } from './removable'
-import { canFetch, Retryer } from './retryer'
+import { canFetch, Retryer, createRetryer } from './retryer'
 import { noop } from './utils'
 
 // TYPES
@@ -90,7 +90,7 @@ export class Mutation<
 
   private observers: MutationObserver<TData, TError, TVariables, TContext>[]
   private mutationCache: MutationCache
-  private retryer?: Retryer<TData, TError>
+  private retryer?: Retryer<TData>
 
   constructor(config: MutationConfig<TData, TError, TVariables, TContext>) {
     super()
@@ -262,7 +262,7 @@ export class Mutation<
   }
 
   private executeMutation(): Promise<TData> {
-    this.retryer = new Retryer({
+    this.retryer = createRetryer({
       fn: () => {
         if (!this.options.mutationFn) {
           return Promise.reject('No mutationFn found')
diff --git a/src/core/notifyManager.ts b/src/core/notifyManager.ts
index b584f7bf8..dc1e7a0ca 100644
--- a/src/core/notifyManager.ts
+++ b/src/core/notifyManager.ts
@@ -8,43 +8,32 @@ type NotifyFunction = (callback: () => void) => void
 
 type BatchNotifyFunction = (callback: () => void) => void
 
-// CLASS
-
-export class NotifyManager {
-  private queue: NotifyCallback[]
-  private transactions: number
-  private notifyFn: NotifyFunction
-  private batchNotifyFn: BatchNotifyFunction
-
-  constructor() {
-    this.queue = []
-    this.transactions = 0
-
-    this.notifyFn = (callback: () => void) => {
-      callback()
-    }
-
-    this.batchNotifyFn = (callback: () => void) => {
-      callback()
-    }
+export function createNotifyManager() {
+  let queue: NotifyCallback[] = []
+  let transactions = 0
+  let notifyFn: NotifyFunction = callback => {
+    callback()
+  }
+  let batchNotifyFn: BatchNotifyFunction = (callback: () => void) => {
+    callback()
   }
 
-  batch<T>(callback: () => T): T {
-    this.transactions++
+  const batch = <T>(callback: () => T): T => {
+    transactions++
     const result = callback()
-    this.transactions--
-    if (!this.transactions) {
-      this.flush()
+    transactions--
+    if (!transactions) {
+      flush()
     }
     return result
   }
 
-  schedule(callback: NotifyCallback): void {
-    if (this.transactions) {
-      this.queue.push(callback)
+  const schedule = (callback: NotifyCallback): void => {
+    if (transactions) {
+      queue.push(callback)
     } else {
       scheduleMicrotask(() => {
-        this.notifyFn(callback)
+        notifyFn(callback)
       })
     }
   }
@@ -52,22 +41,22 @@ export class NotifyManager {
   /**
    * All calls to the wrapped function will be batched.
    */
-  batchCalls<T extends Function>(callback: T): T {
+  const batchCalls = <T extends Function>(callback: T): T => {
     return ((...args: any[]) => {
-      this.schedule(() => {
+      schedule(() => {
         callback(...args)
       })
     }) as any
   }
 
-  flush(): void {
-    const queue = this.queue
-    this.queue = []
-    if (queue.length) {
+  const flush = (): void => {
+    const originalQueue = queue
+    queue = []
+    if (originalQueue.length) {
       scheduleMicrotask(() => {
-        this.batchNotifyFn(() => {
-          queue.forEach(callback => {
-            this.notifyFn(callback)
+        batchNotifyFn(() => {
+          originalQueue.forEach(callback => {
+            notifyFn(callback)
           })
         })
       })
@@ -78,19 +67,26 @@ export class NotifyManager {
    * Use this method to set a custom notify function.
    * This can be used to for example wrap notifications with `React.act` while running tests.
    */
-  setNotifyFunction(fn: NotifyFunction) {
-    this.notifyFn = fn
+  const setNotifyFunction = (fn: NotifyFunction) => {
+    notifyFn = fn
   }
 
   /**
    * Use this method to set a custom function to batch notifications together into a single tick.
    * By default React Query will use the batch function provided by ReactDOM or React Native.
    */
-  setBatchNotifyFunction(fn: BatchNotifyFunction) {
-    this.batchNotifyFn = fn
+  const setBatchNotifyFunction = (fn: BatchNotifyFunction) => {
+    batchNotifyFn = fn
   }
+
+  return {
+    batch,
+    batchCalls,
+    schedule,
+    setNotifyFunction,
+    setBatchNotifyFunction,
+  } as const
 }
 
 // SINGLETON
-
-export const notifyManager = new NotifyManager()
+export const notifyManager = createNotifyManager()
diff --git a/src/core/query.ts b/src/core/query.ts
index 12e99622e..07e171ecc 100644
--- a/src/core/query.ts
+++ b/src/core/query.ts
@@ -21,7 +21,7 @@ import type { QueryCache } from './queryCache'
 import type { QueryObserver } from './queryObserver'
 import { notifyManager } from './notifyManager'
 import { getLogger } from './logger'
-import { Retryer, isCancelledError, canFetch } from './retryer'
+import { Retryer, isCancelledError, canFetch, createRetryer } from './retryer'
 import { Removable } from './removable'
 
 // TYPES
@@ -158,7 +158,7 @@ export class Query<
 
   private cache: QueryCache
   private promise?: Promise<TData>
-  private retryer?: Retryer<TData, TError>
+  private retryer?: Retryer<TData>
   private observers: QueryObserver<any, any, any, any, any>[]
   private defaultOptions?: QueryOptions<TQueryFnData, TError, TData, TQueryKey>
   private abortSignalConsumed: boolean
@@ -431,7 +431,7 @@ export class Query<
     }
 
     // Try to fetch the data
-    this.retryer = new Retryer({
+    this.retryer = createRetryer({
       fn: context.fetchFn as () => TData,
       abort: abortController?.abort?.bind(abortController),
       onSuccess: data => {
diff --git a/src/core/retryer.ts b/src/core/retryer.ts
index 537f1b28a..0b5f8bf85 100644
--- a/src/core/retryer.ts
+++ b/src/core/retryer.ts
@@ -18,6 +18,14 @@ interface RetryerConfig<TData = unknown, TError = unknown> {
   networkMode: NetworkMode | undefined
 }
 
+export interface Retryer<TData = unknown> {
+  promise: Promise<TData>
+  cancel: (cancelOptions?: CancelOptions) => void
+  continue: () => void
+  cancelRetry: () => void
+  continueRetry: () => void
+}
+
 export type RetryValue<TError> = boolean | number | ShouldRetryFunction<TError>
 
 type ShouldRetryFunction<TError> = (
@@ -55,160 +63,152 @@ export function isCancelledError(value: any): value is CancelledError {
   return value instanceof CancelledError
 }
 
-// CLASS
-
-export class Retryer<TData = unknown, TError = unknown> {
-  cancel: (options?: CancelOptions) => void
-  cancelRetry: () => void
-  continueRetry: () => void
-  continue: () => void
-  failureCount: number
-  isPaused: boolean
-  isResolved: boolean
-  promise: Promise<TData>
-
-  constructor(config: RetryerConfig<TData, TError>) {
-    let cancelRetry = false
-    let continueFn: ((value?: unknown) => void) | undefined
-    let promiseResolve: (data: TData) => void
-    let promiseReject: (error: TError) => void
-
-    this.cancel = (cancelOptions?: CancelOptions): void => {
-      if (!this.isResolved) {
-        reject(new CancelledError(cancelOptions))
-
-        config.abort?.()
-      }
-    }
-    this.cancelRetry = () => {
-      cancelRetry = true
+export function createRetryer<TData = unknown, TError = unknown>(
+  config: RetryerConfig<TData, TError>
+): Retryer<TData> {
+  let isRetryCancelled = false
+  let failureCount = 0
+  let isResolved = false
+  const promise = new Promise<TData>((outerResolve, outerReject) => {
+    promiseResolve = outerResolve
+    promiseReject = outerReject
+  })
+  let continueFn: ((value?: unknown) => void) | undefined
+  let promiseResolve: (data: TData) => void
+  let promiseReject: (error: TError) => void
+
+  const cancel = (cancelOptions?: CancelOptions): void => {
+    if (!isResolved) {
+      reject(new CancelledError(cancelOptions))
+
+      config.abort?.()
     }
+  }
+  const cancelRetry = () => {
+    isRetryCancelled = true
+  }
 
-    this.continueRetry = () => {
-      cancelRetry = false
-    }
+  const continueRetry = () => {
+    isRetryCancelled = false
+  }
 
-    const shouldPause = () =>
-      !focusManager.isFocused() ||
-      (config.networkMode !== 'always' && !onlineManager.isOnline())
+  const shouldPause = () =>
+    !focusManager.isFocused() ||
+    (config.networkMode !== 'always' && !onlineManager.isOnline())
 
-    this.continue = () => {
+  const resolve = (value: any) => {
+    if (!isResolved) {
+      isResolved = true
+      config.onSuccess?.(value)
       continueFn?.()
+      promiseResolve(value)
     }
-    this.failureCount = 0
-    this.isPaused = false
-    this.isResolved = false
-    this.promise = new Promise<TData>((outerResolve, outerReject) => {
-      promiseResolve = outerResolve
-      promiseReject = outerReject
-    })
+  }
 
-    const resolve = (value: any) => {
-      if (!this.isResolved) {
-        this.isResolved = true
-        config.onSuccess?.(value)
-        continueFn?.()
-        promiseResolve(value)
-      }
+  const reject = (value: any) => {
+    if (!isResolved) {
+      isResolved = true
+      config.onError?.(value)
+      continueFn?.()
+      promiseReject(value)
     }
+  }
 
-    const reject = (value: any) => {
-      if (!this.isResolved) {
-        this.isResolved = true
-        config.onError?.(value)
-        continueFn?.()
-        promiseReject(value)
+  const pause = () => {
+    return new Promise(continueResolve => {
+      continueFn = value => {
+        if (isResolved || !shouldPause()) {
+          return continueResolve(value)
+        }
+      }
+      config.onPause?.()
+    }).then(() => {
+      continueFn = undefined
+      if (!isResolved) {
+        config.onContinue?.()
       }
+    })
+  }
+
+  // Create loop function
+  const run = () => {
+    // Do nothing if already resolved
+    if (isResolved) {
+      return
     }
 
-    const pause = () => {
-      return new Promise(continueResolve => {
-        continueFn = value => {
-          if (this.isResolved || !shouldPause()) {
-            return continueResolve(value)
-          }
-        }
-        this.isPaused = true
-        config.onPause?.()
-      }).then(() => {
-        continueFn = undefined
-        this.isPaused = false
-        if (!this.isResolved) {
-          config.onContinue?.()
-        }
-      })
+    let promiseOrValue: any
+
+    // Execute query
+    try {
+      promiseOrValue = config.fn()
+    } catch (error) {
+      promiseOrValue = Promise.reject(error)
     }
 
-    // Create loop function
-    const run = () => {
-      // Do nothing if already resolved
-      if (this.isResolved) {
-        return
-      }
+    Promise.resolve(promiseOrValue)
+      .then(resolve)
+      .catch(error => {
+        // Stop if the fetch is already resolved
+        if (isResolved) {
+          return
+        }
 
-      let promiseOrValue: any
+        // Do we need to retry the request?
+        const retry = config.retry ?? 3
+        const retryDelay = config.retryDelay ?? defaultRetryDelay
+        const delay =
+          typeof retryDelay === 'function'
+            ? retryDelay(failureCount, error)
+            : retryDelay
+        const shouldRetry =
+          retry === true ||
+          (typeof retry === 'number' && failureCount < retry) ||
+          (typeof retry === 'function' && retry(failureCount, error))
+
+        if (isRetryCancelled || !shouldRetry) {
+          // We are done if the query does not need to be retried
+          reject(error)
+          return
+        }
 
-      // Execute query
-      try {
-        promiseOrValue = config.fn()
-      } catch (error) {
-        promiseOrValue = Promise.reject(error)
-      }
+        failureCount++
+
+        // Notify on fail
+        config.onFail?.(failureCount, error)
+
+        // Delay
+        sleep(delay)
+          // Pause if the document is not visible or when the device is offline
+          .then(() => {
+            if (shouldPause()) {
+              return pause()
+            }
+          })
+          .then(() => {
+            if (isRetryCancelled) {
+              reject(error)
+            } else {
+              run()
+            }
+          })
+      })
+  }
 
-      Promise.resolve(promiseOrValue)
-        .then(resolve)
-        .catch(error => {
-          // Stop if the fetch is already resolved
-          if (this.isResolved) {
-            return
-          }
-
-          // Do we need to retry the request?
-          const retry = config.retry ?? 3
-          const retryDelay = config.retryDelay ?? defaultRetryDelay
-          const delay =
-            typeof retryDelay === 'function'
-              ? retryDelay(this.failureCount, error)
-              : retryDelay
-          const shouldRetry =
-            retry === true ||
-            (typeof retry === 'number' && this.failureCount < retry) ||
-            (typeof retry === 'function' && retry(this.failureCount, error))
-
-          if (cancelRetry || !shouldRetry) {
-            // We are done if the query does not need to be retried
-            reject(error)
-            return
-          }
-
-          this.failureCount++
-
-          // Notify on fail
-          config.onFail?.(this.failureCount, error)
-
-          // Delay
-          sleep(delay)
-            // Pause if the document is not visible or when the device is offline
-            .then(() => {
-              if (shouldPause()) {
-                return pause()
-              }
-            })
-            .then(() => {
-              if (cancelRetry) {
-                reject(error)
-              } else {
-                run()
-              }
-            })
-        })
-    }
+  // Start loop
+  if (canFetch(config.networkMode)) {
+    run()
+  } else {
+    pause().then(run)
+  }
 
-    // Start loop
-    if (canFetch(config.networkMode)) {
-      run()
-    } else {
-      pause().then(run)
-    }
+  return {
+    promise,
+    cancel,
+    continue: () => {
+      continueFn?.()
+    },
+    cancelRetry,
+    continueRetry,
   }
 }
diff --git a/src/reactjs/useQueries.ts b/src/reactjs/useQueries.ts
index 339f6a093..8f5001e12 100644
--- a/src/reactjs/useQueries.ts
+++ b/src/reactjs/useQueries.ts
@@ -110,9 +110,11 @@ type QueriesResults<
   : // Fallback
     UseQueryResult[]
 
-export function useQueries<T extends any[]>(
+export function useQueries<T extends any[]>({
+  queries,
+}: {
   queries: readonly [...QueriesOptions<T>]
-): QueriesResults<T> {
+}): QueriesResults<T> {
   const mountedRef = React.useRef(false)
   const [, forceUpdate] = React.useState(0)
 


Test patch:
diff --git a/src/core/tests/notifyManager.test.tsx b/src/core/tests/notifyManager.test.tsx
index 53816aa76..fc24c2c8c 100644
--- a/src/core/tests/notifyManager.test.tsx
+++ b/src/core/tests/notifyManager.test.tsx
@@ -1,9 +1,9 @@
-import { NotifyManager } from '../notifyManager'
+import { createNotifyManager } from '../notifyManager'
 import { sleep } from '../../devtools/tests/utils'
 
 describe('notifyManager', () => {
   it('should use default notifyFn', async () => {
-    const notifyManagerTest = new NotifyManager()
+    const notifyManagerTest = createNotifyManager()
     const callbackSpy = jest.fn()
     notifyManagerTest.schedule(callbackSpy)
     await sleep(1)
@@ -11,7 +11,7 @@ describe('notifyManager', () => {
   })
 
   it('should use default batchNotifyFn', async () => {
-    const notifyManagerTest = new NotifyManager()
+    const notifyManagerTest = createNotifyManager()
     const callbackScheduleSpy = jest
       .fn()
       .mockImplementation(async () => await sleep(20))
diff --git a/src/reactjs/tests/suspense.test.tsx b/src/reactjs/tests/suspense.test.tsx
index 45e35d70d..fefff96e4 100644
--- a/src/reactjs/tests/suspense.test.tsx
+++ b/src/reactjs/tests/suspense.test.tsx
@@ -103,8 +103,8 @@ describe("useQuery's in Suspense mode", () => {
     fireEvent.click(rendered.getByText('next'))
     await sleep(10)
 
-    expect(states.length).toBe(3)
-    expect(states[2]).toMatchObject({
+    expect(states.length).toBe(2)
+    expect(states[1]).toMatchObject({
       data: { pages: [2], pageParams: [undefined] },
       status: 'success',
     })
diff --git a/src/reactjs/tests/useInfiniteQuery.test.tsx b/src/reactjs/tests/useInfiniteQuery.test.tsx
index 1149602b0..1b15cdb5e 100644
--- a/src/reactjs/tests/useInfiniteQuery.test.tsx
+++ b/src/reactjs/tests/useInfiniteQuery.test.tsx
@@ -15,6 +15,7 @@ import {
   QueryClient,
   QueryCache,
   QueryFunctionContext,
+  InfiniteData,
 } from '../..'
 
 interface Result {
@@ -298,6 +299,41 @@ describe('useInfiniteQuery', () => {
     })
   })
 
+  it('should be able to select a new result and not cause infinite renders', async () => {
+    const key = queryKey()
+    const states: UseInfiniteQueryResult<{ count: number; id: number }>[] = []
+    let selectCalled = 0
+
+    function Page() {
+      const state = useInfiniteQuery(key, () => ({ count: 1 }), {
+        select: React.useCallback((data: InfiniteData<{ count: number }>) => {
+          selectCalled++
+          return {
+            pages: data.pages.map(x => ({ ...x, id: Math.random() })),
+            pageParams: data.pageParams,
+          }
+        }, []),
+      })
+      states.push(state)
+      return null
+    }
+
+    renderWithClient(queryClient, <Page />)
+
+    await sleep(20)
+
+    expect(states.length).toBe(2)
+    expect(selectCalled).toBe(1)
+    expect(states[0]).toMatchObject({
+      data: undefined,
+      isSuccess: false,
+    })
+    expect(states[1]).toMatchObject({
+      data: { pages: [{ count: 1 }] },
+      isSuccess: true,
+    })
+  })
+
   it('should be able to reverse the data', async () => {
     const key = queryKey()
     const states: UseInfiniteQueryResult<number>[] = []
diff --git a/src/reactjs/tests/useQueries.test.tsx b/src/reactjs/tests/useQueries.test.tsx
index 0b549747d..f6d260cbb 100644
--- a/src/reactjs/tests/useQueries.test.tsx
+++ b/src/reactjs/tests/useQueries.test.tsx
@@ -30,22 +30,24 @@ describe('useQueries', () => {
     const results: UseQueryResult[][] = []
 
     function Page() {
-      const result = useQueries([
-        {
-          queryKey: key1,
-          queryFn: async () => {
-            await sleep(5)
-            return 1
+      const result = useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: async () => {
+              await sleep(5)
+              return 1
+            },
           },
-        },
-        {
-          queryKey: key2,
-          queryFn: async () => {
-            await sleep(10)
-            return 2
+          {
+            queryKey: key2,
+            queryFn: async () => {
+              await sleep(10)
+              return 2
+            },
           },
-        },
-      ])
+        ],
+      })
       results.push(result)
       return null
     }
@@ -67,24 +69,26 @@ describe('useQueries', () => {
 
     function Page() {
       const [count, setCount] = React.useState(1)
-      const result = useQueries([
-        {
-          queryKey: [key1, count],
-          keepPreviousData: true,
-          queryFn: async () => {
-            await sleep(5)
-            return count * 2
+      const result = useQueries({
+        queries: [
+          {
+            queryKey: [key1, count],
+            keepPreviousData: true,
+            queryFn: async () => {
+              await sleep(5)
+              return count * 2
+            },
           },
-        },
-        {
-          queryKey: [key2, count],
-          keepPreviousData: true,
-          queryFn: async () => {
-            await sleep(10)
-            return count * 5
+          {
+            queryKey: [key2, count],
+            keepPreviousData: true,
+            queryFn: async () => {
+              await sleep(10)
+              return count * 5
+            },
           },
-        },
-      ])
+        ],
+      })
       states.push(result)
 
       React.useEffect(() => {
@@ -151,16 +155,16 @@ describe('useQueries', () => {
 
     function Page() {
       const [count, setCount] = React.useState(2)
-      const result = useQueries(
-        Array.from({ length: count }, (_, i) => ({
+      const result = useQueries({
+        queries: Array.from({ length: count }, (_, i) => ({
           queryKey: [key, count, i + 1],
           keepPreviousData: true,
           queryFn: async () => {
             await sleep(5 * (i + 1))
             return (i + 1) * count * 2
           },
-        }))
-      )
+        })),
+      })
 
       states.push(result)
 
@@ -261,8 +265,8 @@ describe('useQueries', () => {
       const [series2, setSeries2] = React.useState(2)
       const ids = [series1, series2]
 
-      const result = useQueries(
-        ids.map(id => {
+      const result = useQueries({
+        queries: ids.map(id => {
           return {
             queryKey: [key, id],
             queryFn: async () => {
@@ -271,8 +275,8 @@ describe('useQueries', () => {
             },
             keepPreviousData: true,
           }
-        })
-      )
+        }),
+      })
 
       states.push(result)
 
@@ -356,8 +360,8 @@ describe('useQueries', () => {
       const [enableId1, setEnableId1] = React.useState(true)
       const ids = enableId1 ? [1, 2] : [2]
 
-      const result = useQueries(
-        ids.map(id => {
+      const result = useQueries({
+        queries: ids.map(id => {
           return {
             queryKey: [key, id],
             queryFn: async () => {
@@ -366,8 +370,8 @@ describe('useQueries', () => {
             },
             keepPreviousData: true,
           }
-        })
-      )
+        }),
+      })
 
       states.push(result)
 
@@ -443,20 +447,22 @@ describe('useQueries', () => {
     // @ts-expect-error (Page component is not rendered)
     // eslint-disable-next-line
     function Page() {
-      const result1 = useQueries<[[number], [string], [string[], boolean]]>([
-        {
-          queryKey: key1,
-          queryFn: () => 1,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-        },
-        {
-          queryKey: key3,
-          queryFn: () => ['string[]'],
-        },
-      ])
+      const result1 = useQueries<[[number], [string], [string[], boolean]]>({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 1,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+          },
+          {
+            queryKey: key3,
+            queryFn: () => ['string[]'],
+          },
+        ],
+      })
       expectType<QueryObserverResult<number, unknown>>(result1[0])
       expectType<QueryObserverResult<string, unknown>>(result1[1])
       expectType<QueryObserverResult<string[], boolean>>(result1[2])
@@ -468,80 +474,86 @@ describe('useQueries', () => {
       // TData (3rd element) takes precedence over TQueryFnData (1st element)
       const result2 = useQueries<
         [[string, unknown, string], [string, unknown, number]]
-      >([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return a.toLowerCase()
-          },
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return parseInt(a)
-          },
-        },
-      ])
+      >({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return a.toLowerCase()
+            },
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return parseInt(a)
+            },
+          },
+        ],
+      })
       expectType<QueryObserverResult<string, unknown>>(result2[0])
       expectType<QueryObserverResult<number, unknown>>(result2[1])
       expectType<string | undefined>(result2[0].data)
       expectType<number | undefined>(result2[1].data)
 
       // types should be enforced
-      useQueries<[[string, unknown, string], [string, boolean, number]]>([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return a.toLowerCase()
-          },
-          onSuccess: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-          },
-          placeholderData: 'string',
-          // @ts-expect-error (initialData: string)
-          initialData: 123,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return parseInt(a)
-          },
-          onSuccess: a => {
-            expectType<number>(a)
-            expectTypeNotAny(a)
-          },
-          onError: e => {
-            expectType<boolean>(e)
-            expectTypeNotAny(e)
-          },
-          placeholderData: 'string',
-          // @ts-expect-error (initialData: string)
-          initialData: 123,
-        },
-      ])
+      useQueries<[[string, unknown, string], [string, boolean, number]]>({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return a.toLowerCase()
+            },
+            onSuccess: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+            },
+            placeholderData: 'string',
+            // @ts-expect-error (initialData: string)
+            initialData: 123,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return parseInt(a)
+            },
+            onSuccess: a => {
+              expectType<number>(a)
+              expectTypeNotAny(a)
+            },
+            onError: e => {
+              expectType<boolean>(e)
+              expectTypeNotAny(e)
+            },
+            placeholderData: 'string',
+            // @ts-expect-error (initialData: string)
+            initialData: 123,
+          },
+        ],
+      })
 
       // field names should be enforced
-      useQueries<[[string]]>([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          // @ts-expect-error (invalidField)
-          someInvalidField: [],
-        },
-      ])
+      useQueries<[[string]]>({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            // @ts-expect-error (invalidField)
+            someInvalidField: [],
+          },
+        ],
+      })
     }
   })
 
@@ -559,20 +571,22 @@ describe('useQueries', () => {
           { queryFnData: string },
           { queryFnData: string[]; error: boolean }
         ]
-      >([
-        {
-          queryKey: key1,
-          queryFn: () => 1,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-        },
-        {
-          queryKey: key3,
-          queryFn: () => ['string[]'],
-        },
-      ])
+      >({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 1,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+          },
+          {
+            queryKey: key3,
+            queryFn: () => ['string[]'],
+          },
+        ],
+      })
       expectType<QueryObserverResult<number, unknown>>(result1[0])
       expectType<QueryObserverResult<string, unknown>>(result1[1])
       expectType<QueryObserverResult<string[], boolean>>(result1[2])
@@ -587,52 +601,56 @@ describe('useQueries', () => {
           { queryFnData: string; data: string },
           { queryFnData: string; data: number }
         ]
-      >([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return a.toLowerCase()
-          },
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return parseInt(a)
-          },
-        },
-      ])
+      >({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return a.toLowerCase()
+            },
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return parseInt(a)
+            },
+          },
+        ],
+      })
       expectType<QueryObserverResult<string, unknown>>(result2[0])
       expectType<QueryObserverResult<number, unknown>>(result2[1])
       expectType<string | undefined>(result2[0].data)
       expectType<number | undefined>(result2[1].data)
 
       // can pass only TData (data prop) although TQueryFnData will be left unknown
-      const result3 = useQueries<[{ data: string }, { data: number }]>([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<unknown>(a)
-            expectTypeNotAny(a)
-            return a as string
-          },
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<unknown>(a)
-            expectTypeNotAny(a)
-            return a as number
-          },
-        },
-      ])
+      const result3 = useQueries<[{ data: string }, { data: number }]>({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<unknown>(a)
+              expectTypeNotAny(a)
+              return a as string
+            },
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<unknown>(a)
+              expectTypeNotAny(a)
+              return a as number
+            },
+          },
+        ],
+      })
       expectType<QueryObserverResult<string, unknown>>(result3[0])
       expectType<QueryObserverResult<number, unknown>>(result3[1])
       expectType<string | undefined>(result3[0].data)
@@ -644,54 +662,58 @@ describe('useQueries', () => {
           { queryFnData: string; data: string },
           { queryFnData: string; data: number; error: boolean }
         ]
-      >([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return a.toLowerCase()
-          },
-          onSuccess: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-          },
-          placeholderData: 'string',
-          // @ts-expect-error (initialData: string)
-          initialData: 123,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          select: a => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-            return parseInt(a)
-          },
-          onSuccess: a => {
-            expectType<number>(a)
-            expectTypeNotAny(a)
-          },
-          onError: e => {
-            expectType<boolean>(e)
-            expectTypeNotAny(e)
-          },
-          placeholderData: 'string',
-          // @ts-expect-error (initialData: string)
-          initialData: 123,
-        },
-      ])
+      >({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return a.toLowerCase()
+            },
+            onSuccess: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+            },
+            placeholderData: 'string',
+            // @ts-expect-error (initialData: string)
+            initialData: 123,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            select: a => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+              return parseInt(a)
+            },
+            onSuccess: a => {
+              expectType<number>(a)
+              expectTypeNotAny(a)
+            },
+            onError: e => {
+              expectType<boolean>(e)
+              expectTypeNotAny(e)
+            },
+            placeholderData: 'string',
+            // @ts-expect-error (initialData: string)
+            initialData: 123,
+          },
+        ],
+      })
 
       // field names should be enforced
-      useQueries<[{ queryFnData: string }]>([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          // @ts-expect-error (invalidField)
-          someInvalidField: [],
-        },
-      ])
+      useQueries<[{ queryFnData: string }]>({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            // @ts-expect-error (invalidField)
+            someInvalidField: [],
+          },
+        ],
+      })
     }
   })
 
@@ -705,40 +727,42 @@ describe('useQueries', () => {
     // eslint-disable-next-line
     function Page() {
       // Array.map preserves TQueryFnData
-      const result1 = useQueries(
-        Array(50).map((_, i) => ({
+      const result1 = useQueries({
+        queries: Array(50).map((_, i) => ({
           queryKey: ['key', i] as const,
           queryFn: () => i + 10,
-        }))
-      )
+        })),
+      })
       expectType<QueryObserverResult<number, unknown>[]>(result1)
       expectType<number | undefined>(result1[0]?.data)
 
       // Array.map preserves TData
-      const result2 = useQueries(
-        Array(50).map((_, i) => ({
+      const result2 = useQueries({
+        queries: Array(50).map((_, i) => ({
           queryKey: ['key', i] as const,
           queryFn: () => i + 10,
           select: (data: number) => data.toString(),
-        }))
-      )
+        })),
+      })
       expectType<QueryObserverResult<string, unknown>[]>(result2)
 
-      const result3 = useQueries([
-        {
-          queryKey: key1,
-          queryFn: () => 1,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-        },
-        {
-          queryKey: key3,
-          queryFn: () => ['string[]'],
-          select: () => 123,
-        },
-      ])
+      const result3 = useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 1,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+          },
+          {
+            queryKey: key3,
+            queryFn: () => ['string[]'],
+            select: () => 123,
+          },
+        ],
+      })
       expectType<QueryObserverResult<number, unknown>>(result3[0])
       expectType<QueryObserverResult<string, unknown>>(result3[1])
       expectType<QueryObserverResult<number, unknown>>(result3[2])
@@ -748,154 +772,164 @@ describe('useQueries', () => {
       expectType<number | undefined>(result3[2].data)
 
       // initialData/placeholderData are enforced
-      useQueries([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          placeholderData: 'string',
-          // @ts-expect-error (initialData: string)
-          initialData: 123,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 123,
-          // @ts-expect-error (placeholderData: number)
-          placeholderData: 'string',
-          initialData: 123,
-        },
-      ])
+      useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            placeholderData: 'string',
+            // @ts-expect-error (initialData: string)
+            initialData: 123,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 123,
+            // @ts-expect-error (placeholderData: number)
+            placeholderData: 'string',
+            initialData: 123,
+          },
+        ],
+      })
 
       // select / onSuccess / onSettled params are "indirectly" enforced
-      useQueries([
-        // unfortunately TS will not suggest the type for you
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          // @ts-expect-error (noImplicitAny)
-          onSuccess: a => null,
-          // @ts-expect-error (noImplicitAny)
-          onSettled: a => null,
-        },
-        // however you can add a type to the callback
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          onSuccess: (a: string) => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-          },
-          onSettled: (a: string | undefined) => {
-            expectType<string | undefined>(a)
-            expectTypeNotAny(a)
-          },
-        },
-        // the type you do pass is enforced
-        {
-          queryKey: key3,
-          queryFn: () => 'string',
-          // @ts-expect-error (only accepts string)
-          onSuccess: (a: number) => null,
-        },
-        {
-          queryKey: key4,
-          queryFn: () => 'string',
-          select: (a: string) => parseInt(a),
-          // @ts-expect-error (select is defined => only accepts number)
-          onSuccess: (a: string) => null,
-          onSettled: (a: number | undefined) => {
-            expectType<number | undefined>(a)
-            expectTypeNotAny(a)
-          },
-        },
-      ])
+      useQueries({
+        queries: [
+          // unfortunately TS will not suggest the type for you
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            // @ts-expect-error (noImplicitAny)
+            onSuccess: a => null,
+            // @ts-expect-error (noImplicitAny)
+            onSettled: a => null,
+          },
+          // however you can add a type to the callback
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            onSuccess: (a: string) => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+            },
+            onSettled: (a: string | undefined) => {
+              expectType<string | undefined>(a)
+              expectTypeNotAny(a)
+            },
+          },
+          // the type you do pass is enforced
+          {
+            queryKey: key3,
+            queryFn: () => 'string',
+            // @ts-expect-error (only accepts string)
+            onSuccess: (a: number) => null,
+          },
+          {
+            queryKey: key4,
+            queryFn: () => 'string',
+            select: (a: string) => parseInt(a),
+            // @ts-expect-error (select is defined => only accepts number)
+            onSuccess: (a: string) => null,
+            onSettled: (a: number | undefined) => {
+              expectType<number | undefined>(a)
+              expectTypeNotAny(a)
+            },
+          },
+        ],
+      })
 
       // callbacks are also indirectly enforced with Array.map
-      useQueries(
+      useQueries({
         // @ts-expect-error (onSuccess only accepts string)
-        Array(50).map((_, i) => ({
+        queries: Array(50).map((_, i) => ({
           queryKey: ['key', i] as const,
           queryFn: () => i + 10,
           select: (data: number) => data.toString(),
           onSuccess: (_data: number) => null,
-        }))
-      )
-      useQueries(
-        Array(50).map((_, i) => ({
+        })),
+      })
+      useQueries({
+        queries: Array(50).map((_, i) => ({
           queryKey: ['key', i] as const,
           queryFn: () => i + 10,
           select: (data: number) => data.toString(),
           onSuccess: (_data: string) => null,
-        }))
-      )
+        })),
+      })
 
       // results inference works when all the handlers are defined
-      const result4 = useQueries([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          // @ts-expect-error (noImplicitAny)
-          onSuccess: a => null,
-          // @ts-expect-error (noImplicitAny)
-          onSettled: a => null,
-        },
-        {
-          queryKey: key2,
-          queryFn: () => 'string',
-          onSuccess: (a: string) => {
-            expectType<string>(a)
-            expectTypeNotAny(a)
-          },
-          onSettled: (a: string | undefined) => {
-            expectType<string | undefined>(a)
-            expectTypeNotAny(a)
-          },
-        },
-        {
-          queryKey: key4,
-          queryFn: () => 'string',
-          select: (a: string) => parseInt(a),
-          onSuccess: (_a: number) => null,
-          onSettled: (a: number | undefined) => {
-            expectType<number | undefined>(a)
-            expectTypeNotAny(a)
-          },
-        },
-      ])
+      const result4 = useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            // @ts-expect-error (noImplicitAny)
+            onSuccess: a => null,
+            // @ts-expect-error (noImplicitAny)
+            onSettled: a => null,
+          },
+          {
+            queryKey: key2,
+            queryFn: () => 'string',
+            onSuccess: (a: string) => {
+              expectType<string>(a)
+              expectTypeNotAny(a)
+            },
+            onSettled: (a: string | undefined) => {
+              expectType<string | undefined>(a)
+              expectTypeNotAny(a)
+            },
+          },
+          {
+            queryKey: key4,
+            queryFn: () => 'string',
+            select: (a: string) => parseInt(a),
+            onSuccess: (_a: number) => null,
+            onSettled: (a: number | undefined) => {
+              expectType<number | undefined>(a)
+              expectTypeNotAny(a)
+            },
+          },
+        ],
+      })
       expectType<QueryObserverResult<string, unknown>>(result4[0])
       expectType<QueryObserverResult<string, unknown>>(result4[1])
       expectType<QueryObserverResult<number, unknown>>(result4[2])
 
       // Array as const does not throw error
-      const result5 = useQueries([
-        {
-          queryKey: ['key1'],
-          queryFn: () => 'string',
-        },
-        {
-          queryKey: ['key1'],
-          queryFn: () => 123,
-        },
-      ] as const)
+      const result5 = useQueries({
+        queries: [
+          {
+            queryKey: ['key1'],
+            queryFn: () => 'string',
+          },
+          {
+            queryKey: ['key1'],
+            queryFn: () => 123,
+          },
+        ],
+      } as const)
       expectType<QueryObserverResult<string, unknown>>(result5[0])
       expectType<QueryObserverResult<number, unknown>>(result5[1])
 
       // field names should be enforced - array literal
-      useQueries([
-        {
-          queryKey: key1,
-          queryFn: () => 'string',
-          // @ts-expect-error (invalidField)
-          someInvalidField: [],
-        },
-      ])
+      useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: () => 'string',
+            // @ts-expect-error (invalidField)
+            someInvalidField: [],
+          },
+        ],
+      })
 
       // field names should be enforced - Array.map() result
-      useQueries(
+      useQueries({
         // @ts-expect-error (invalidField)
-        Array(10).map(() => ({
+        queries: Array(10).map(() => ({
           someInvalidField: '',
-        }))
-      )
+        })),
+      })
     }
   })
 
@@ -918,15 +952,17 @@ describe('useQueries', () => {
       })
 
     function Queries() {
-      useQueries([
-        {
-          queryKey: key1,
-          queryFn: async () => {
-            await sleep(10)
-            return 1
+      useQueries({
+        queries: [
+          {
+            queryKey: key1,
+            queryFn: async () => {
+              await sleep(10)
+              return 1
+            },
           },
-        },
-      ])
+        ],
+      })
 
       return (
         <div>