Repository: TanStack/query
Pull request #5670 (2023-07-03 20:41:21)

docs(ssr): Updated note about TS error with async server components
This issue has been fixed now in https://github.com/vercel/next.js/releases/tag/v13.4.5 

Related PR:
* https://github.com/vercel/next.js/pull/50557
* https://github.com/microsoft/TypeScript/pull/51328
* https://github.com/DefinitelyTyped/DefinitelyTyped/pull/65220

vercel[bot]: [vc]: #z05yDV/gpOcqG/OLxFjKvdi7K2UOQfF4QChC5LPFsxE=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJxdWVyeSIsInJvb3REaXJlY3RvcnkiOiJkb2NzIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6IiJ9LCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vdGFuc3RhY2svcXVlcnkvMkRiWW5lb3l5ZGt2dlFEZWFtNno0d0JjdzZXdCIsIm5leHRDb21taXRTdGF0dXMiOiJQRU5ESU5HIn1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **query** | 🔄 Building ([Inspect](https://vercel.com/tanstack/query/2DbYneoyydkvvQDeam6z4wBcw6Wt)) |  |  | Jul 3, 2023 8:41pm |


codesandbox-ci[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/TanStack/query/pr/5670/builds/394634) or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 4381ebdd52299cea968e36589280ea3abb150336:

|Sandbox| Source |
|--|--|
|[@tanstack/query-example-react-basic-typescript](https://codesandbox.io/s/tanstack-query-example-react-basic-typescript-27qg9q)| Configuration |
|[@tanstack/query-example-solid-basic-typescript](https://codesandbox.io/s/tanstack-query-example-solid-basic-typescript-3fsx86)| Configuration |
|[@tanstack/query-example-svelte-basic](https://codesandbox.io/s/tanstack-query-example-svelte-basic-x7zf3k)| Configuration |
|[@tanstack/query-example-vue-basic](https://codesandbox.io/s/tanstack-query-example-vue-basic-k3mqs5)| Configuration |


Patch:
diff --git a/docs/react/guides/infinite-queries.md b/docs/react/guides/infinite-queries.md
index 53d995184..a7e640373 100644
--- a/docs/react/guides/infinite-queries.md
+++ b/docs/react/guides/infinite-queries.md
@@ -199,7 +199,7 @@ useInfiniteQuery({
 
 ## What if I want to manually update the infinite query?
 
-Manually removing first page:
+### Manually removing first page:
 
 [//]: # 'Example6'
 
@@ -212,7 +212,7 @@ queryClient.setQueryData(['projects'], (data) => ({
 
 [//]: # 'Example6'
 
-Manually removing a single value from an individual page:
+### Manually removing a single value from an individual page:
 
 [//]: # 'Example7'
 
@@ -230,4 +230,17 @@ queryClient.setQueryData(['projects'], (data) => ({
 
 [//]: # 'Example7'
 
-Make sure to keep the same data structure of pages and pageParams!
+### Keep only the first page:
+
+[//]: # 'Example8'
+
+```tsx
+queryClient.setQueryData(['projects'], (data) => ({
+  pages: data.pages.slice(0,1),
+  pageParams: data.pageParams.slice(0,1),
+}))
+```
+
+[//]: # 'Example8'
+
+Make sure to always keep the same data structure of pages and pageParams!
diff --git a/docs/react/guides/ssr.md b/docs/react/guides/ssr.md
index 87b926743..6e0419bd9 100644
--- a/docs/react/guides/ssr.md
+++ b/docs/react/guides/ssr.md
@@ -417,7 +417,7 @@ Fetch your data in a Server Component higher up in the component tree than the C
 - Wrap the component tree that needs the prefetched queries inside `<Hydrate>`, and provide it with the dehydrated state
 - You can fetch inside multiple Server Components and use `<Hydrate>` in multiple places
 
-> NOTE: TypeScript currently complains of a type error when using async Server Components. As a temporary workaround, use `{/* @ts-expect-error Server Component */}` when calling this component inside another. For more information, see [End-to-End Type Safety](https://beta.nextjs.org/docs/configuring/typescript#end-to-end-type-safety) in the Next.js 13 beta docs.
+> NOTE: If you encounter a type error while using async Server Components with TypeScript versions lower than `5.1.3` and `@types/react` versions lower than `18.2.8`, it is recommended to update to the latest versions of both. Alternatively, you can use the temporary workaround of adding `{/* @ts-expect-error Server Component */}` when calling this component inside another. For more information, see [Async Server Component TypeScript Error](https://nextjs.org/docs/app/building-your-application/configuring/typescript#async-server-component-typescript-error) in the Next.js 13 docs.
 
 ```tsx
 // app/hydratedPosts.jsx
diff --git a/docs/react/reference/useMutation.md b/docs/react/reference/useMutation.md
index 6fd0f46f9..50233b23c 100644
--- a/docs/react/reference/useMutation.md
+++ b/docs/react/reference/useMutation.md
@@ -51,7 +51,7 @@ mutate(variables, {
   - If set to `Infinity`, will disable garbage collection
 - `mutationKey: unknown[]`
   - Optional
-  - A mutation key can be set to inherit defaults set with `queryClient.setMutationDefaults` or to identify the mutation in the devtools.
+  - A mutation key can be set to inherit defaults set with `queryClient.setMutationDefaults`.
 - `networkMode: 'online' | 'always' | 'offlineFirst`
   - optional
   - defaults to `'online'`
diff --git a/docs/vue/devtools.md b/docs/vue/devtools.md
index 44b6d09f0..f6a93fb08 100644
--- a/docs/vue/devtools.md
+++ b/docs/vue/devtools.md
@@ -7,7 +7,7 @@ Wave your hands in the air and shout hooray because Vue Query comes with dedicat
 
 When you begin your Vue Query journey, you'll want these devtools by your side. They help visualize all of the inner workings of Vue Query and will likely save you hours of debugging if you find yourself in a pinch!
 
-The only thing you need to do is to install official **[Vue Devtools](https://devtools.vuejs.org/guide/installation.html)**
+The only thing you need to do is to install the official **[Vue Devtools](https://devtools.vuejs.org/guide/installation.html)**.
 
-Vue Query will seemingly integrate with official devtools, adding custom inspector and timeline events.
-Devtools would be treeshaken from production bundles by default.
+Vue Query will seamlessly integrate with the official devtools, adding custom inspector and timeline events.
+Devtool code will be treeshaken from production bundles by default.
diff --git a/docs/vue/guides/infinite-queries.md b/docs/vue/guides/infinite-queries.md
index 18dc2cbf4..e84309d52 100644
--- a/docs/vue/guides/infinite-queries.md
+++ b/docs/vue/guides/infinite-queries.md
@@ -71,18 +71,3 @@ const skipToCursor50 = () => fetchNextPage({ pageParam: 50 })
 ```
 
 [//]: # 'Example3'
-[//]: # 'Example7'
-
-```tsx
-const newPagesArray =
-  oldPagesArray?.pages.map((page) =>
-    page.filter((val) => val.id !== updatedId),
-  ) ?? []
-
-queryClient.setQueryData(['projects'], (data) => ({
-  pages: newPagesArray,
-  pageParams: data.pageParams,
-}))
-```
-
-[//]: # 'Example7'
diff --git a/package.json b/package.json
index 9aef78282..335f969b2 100644
--- a/package.json
+++ b/package.json
@@ -76,7 +76,8 @@
     "jest": "^27.5.1",
     "jsonfile": "^6.1.0",
     "luxon": "^2.3.2",
-    "nx": "16.0.1",
+    "nx": "16.5.0",
+    "nx-cloud": "16.0.5",
     "prettier": "^2.6.2",
     "prettier-plugin-svelte": "^2.9.0",
     "react": "^18.2.0",
@@ -96,8 +97,7 @@
     "ts-jest": "^27.1.1",
     "ts-node": "^10.7.0",
     "typescript": "^4.7.4",
-    "vue": "^3.2.33",
-    "nx-cloud": "16.0.5"
+    "vue": "^3.2.33"
   },
   "bundlewatch": {
     "files": [
diff --git a/packages/query-async-storage-persister/package.json b/packages/query-async-storage-persister/package.json
index a20353908..9d4687469 100644
--- a/packages/query-async-storage-persister/package.json
+++ b/packages/query-async-storage-persister/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/query-async-storage-persister",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "A persister for asynchronous storages, to be used with TanStack/Query",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/query-broadcast-client-experimental/package.json b/packages/query-broadcast-client-experimental/package.json
index 600d5f7c4..6213a829f 100644
--- a/packages/query-broadcast-client-experimental/package.json
+++ b/packages/query-broadcast-client-experimental/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/query-broadcast-client-experimental",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "An experimental plugin to for broadcasting the state of your queryClient between browser tabs/windows",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/query-core/package.json b/packages/query-core/package.json
index 4dbd6f5ed..8fccad5d8 100644
--- a/packages/query-core/package.json
+++ b/packages/query-core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/query-core",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "The framework agnostic core that powers TanStack Query",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/query-core/src/focusManager.ts b/packages/query-core/src/focusManager.ts
index 710a009c7..f2d45e6a8 100644
--- a/packages/query-core/src/focusManager.ts
+++ b/packages/query-core/src/focusManager.ts
@@ -58,9 +58,9 @@ export class FocusManager extends Subscribable {
   }
 
   setFocused(focused?: boolean): void {
-    this.focused = focused
-
-    if (focused) {
+    const changed = this.focused !== focused
+    if (changed) {
+      this.focused = focused
       this.onFocus()
     }
   }
diff --git a/packages/query-core/src/onlineManager.ts b/packages/query-core/src/onlineManager.ts
index c42a948a0..f7049f40d 100644
--- a/packages/query-core/src/onlineManager.ts
+++ b/packages/query-core/src/onlineManager.ts
@@ -63,9 +63,10 @@ export class OnlineManager extends Subscribable {
   }
 
   setOnline(online?: boolean): void {
-    this.online = online
+    const changed = this.online !== online
 
-    if (online) {
+    if (changed) {
+      this.online = online
       this.onOnline()
     }
   }
diff --git a/packages/query-core/src/queryObserver.ts b/packages/query-core/src/queryObserver.ts
index 123996e57..4e4ace8f8 100644
--- a/packages/query-core/src/queryObserver.ts
+++ b/packages/query-core/src/queryObserver.ts
@@ -240,7 +240,30 @@ export class QueryObserver<
   ): QueryObserverResult<TData, TError> {
     const query = this.client.getQueryCache().build(this.client, options)
 
-    return this.createResult(query, options)
+    const result = this.createResult(query, options)
+
+    if (shouldAssignObserverCurrentProperties(this, result, options)) {
+      // this assigns the optimistic result to the current Observer
+      // because if the query function changes, useQuery will be performing
+      // an effect where it would fetch again.
+      // When the fetch finishes, we perform a deep data cloning in order
+      // to reuse objects references. This deep data clone is performed against
+      // the `observer.currentResult.data` property
+      // When QueryKey changes, we refresh the query and get new `optimistic`
+      // result, while we leave the `observer.currentResult`, so when new data
+      // arrives, it finds the old `observer.currentResult` which is related
+      // to the old QueryKey. Which means that currentResult and selectData are
+      // out of sync already.
+      // To solve this, we move the cursor of the currentResult everytime
+      // an observer reads an optimistic value.
+
+      // When keeping the previous data, the result doesn't change until new
+      // data arrives.
+      this.currentResult = result
+      this.currentResultOptions = this.options
+      this.currentResultState = this.currentQuery.state
+    }
+    return result
   }
 
   getCurrentResult(): QueryObserverResult<TData, TError> {
@@ -764,3 +787,51 @@ function isStale(
 ): boolean {
   return query.isStaleByTime(options.staleTime)
 }
+
+// this function would decide if we will update the observer's 'current'
+// properties after an optimistic reading via getOptimisticResult
+function shouldAssignObserverCurrentProperties<
+  TQueryFnData = unknown,
+  TError = unknown,
+  TData = TQueryFnData,
+  TQueryData = TQueryFnData,
+  TQueryKey extends QueryKey = QueryKey,
+>(
+  observer: QueryObserver<TQueryFnData, TError, TData, TQueryData, TQueryKey>,
+  optimisticResult: QueryObserverResult<TData, TError>,
+  options: DefaultedQueryObserverOptions<
+    TQueryFnData,
+    TError,
+    TData,
+    TQueryData,
+    TQueryKey
+  >,
+) {
+  // it is important to keep this condition like this for three reasons:
+  // 1. It will get removed in the v5
+  // 2. it reads: don't update the properties if we want to keep the previous
+  // data.
+  // 3. The opposite condition (!options.keepPreviousData) would fallthrough
+  // and will result in a bad decision
+  if (options.keepPreviousData) {
+    return false
+  }
+
+  // this means we want to put some placeholder data when pending and queryKey
+  // changed.
+  if (options.placeholderData !== undefined) {
+    // re-assign properties only if current data is placeholder data
+    // which means that data did not arrive yet, so, if there is some cached data
+    // we need to "prepare" to receive it
+    return optimisticResult.isPlaceholderData
+  }
+
+  // if the newly created result isn't what the observer is holding as current,
+  // then we'll need to update the properties as well
+  if (observer.getCurrentResult() !== optimisticResult) {
+    return true
+  }
+
+  // basically, just keep previous properties if nothing changed
+  return false
+}
diff --git a/packages/query-persist-client-core/package.json b/packages/query-persist-client-core/package.json
index 12020fa5b..25bc92361 100644
--- a/packages/query-persist-client-core/package.json
+++ b/packages/query-persist-client-core/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/query-persist-client-core",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "Set of utilities for interacting with persisters, which can save your queryClient for later use",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/query-sync-storage-persister/package.json b/packages/query-sync-storage-persister/package.json
index 6df21ec06..bf873e403 100644
--- a/packages/query-sync-storage-persister/package.json
+++ b/packages/query-sync-storage-persister/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/query-sync-storage-persister",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "A persister for synchronous storages, to be used with TanStack/Query",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/react-query-devtools/package.json b/packages/react-query-devtools/package.json
index 59c363110..f099797dc 100644
--- a/packages/react-query-devtools/package.json
+++ b/packages/react-query-devtools/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-query-devtools",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "Developer tools to interact with and visualize the TanStack/react-query cache",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/react-query-persist-client/package.json b/packages/react-query-persist-client/package.json
index 6e47452df..8662d76db 100644
--- a/packages/react-query-persist-client/package.json
+++ b/packages/react-query-persist-client/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-query-persist-client",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "React bindings to work with persisters in TanStack/react-query",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/react-query/package.json b/packages/react-query/package.json
index 178fd8073..42e6faf6a 100644
--- a/packages/react-query/package.json
+++ b/packages/react-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/react-query",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "Hooks for managing, caching and syncing asynchronous and remote data in React",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/solid-query/package.json b/packages/solid-query/package.json
index baab8b363..49d44669b 100644
--- a/packages/solid-query/package.json
+++ b/packages/solid-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/solid-query",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "Primitives for managing, caching and syncing asynchronous and remote data in Solid",
   "author": "tannerlinsley",
   "license": "MIT",
diff --git a/packages/svelte-query/package.json b/packages/svelte-query/package.json
index a7e93518b..c5e535e4e 100644
--- a/packages/svelte-query/package.json
+++ b/packages/svelte-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/svelte-query",
-  "version": "4.29.19",
+  "version": "4.29.23",
   "description": "Primitives for managing, caching and syncing asynchronous and remote data in Svelte",
   "author": "Lachlan Collins",
   "license": "MIT",
diff --git a/packages/vue-query/package.json b/packages/vue-query/package.json
index 05f9ecfee..566665ea0 100644
--- a/packages/vue-query/package.json
+++ b/packages/vue-query/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@tanstack/vue-query",
-  "version": "4.29.20",
+  "version": "4.29.23",
   "description": "Hooks for managing, caching and syncing asynchronous and remote data in Vue",
   "author": "Damian Osipiuk",
   "license": "MIT",
diff --git a/packages/vue-query/src/devtools/devtools.ts b/packages/vue-query/src/devtools/devtools.ts
index 4b59a5827..f552bd6e0 100644
--- a/packages/vue-query/src/devtools/devtools.ts
+++ b/packages/vue-query/src/devtools/devtools.ts
@@ -3,7 +3,7 @@
 import { setupDevtoolsPlugin } from '@vue/devtools-api'
 import type { CustomInspectorNode } from '@vue/devtools-api'
 import { rankItem } from '@tanstack/match-sorter-utils'
-import type { Query } from '@tanstack/query-core'
+import type { Query, QueryCacheNotifyEvent } from '@tanstack/query-core'
 import type { QueryClient } from '../queryClient'
 import {
   getQueryStateLabel,
@@ -103,10 +103,15 @@ export function setupDevtools(app: any, queryClient: QueryClient) {
         api.sendInspectorTree(pluginId)
         api.sendInspectorState(pluginId)
 
+        const queryEvents: QueryCacheNotifyEvent['type'][] = [
+          'added',
+          'removed',
+          'updated',
+        ]
         if (
           // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
           event &&
-          ['queryAdded', 'queryRemoved', 'queryUpdated'].includes(event.type)
+          queryEvents.includes(event.type)
         ) {
           api.addTimelineEvent({
             layerId: pluginId,
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 67fae3a63..5122f6c61 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -137,8 +137,8 @@ importers:
         specifier: ^2.3.2
         version: 2.4.0
       nx:
-        specifier: 16.0.1
-        version: 16.0.1
+        specifier: 16.5.0
+        version: 16.5.0
       nx-cloud:
         specifier: 16.0.5
         version: 16.0.5
@@ -2084,7 +2084,7 @@ packages:
     dependencies:
       '@babel/core': 7.9.0
       '@babel/helper-create-class-features-plugin': 7.18.6(@babel/core@7.9.0)
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -2230,7 +2230,7 @@ packages:
       '@babel/core': ^7.0.0-0
     dependencies:
       '@babel/core': 7.9.0
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
       '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.9.0)
     dev: true
 
@@ -2911,7 +2911,7 @@ packages:
     dependencies:
       '@babel/core': 7.9.0
       '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.6
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
     dev: true
 
   /@babel/plugin-transform-flow-strip-types@7.18.6(@babel/core@7.19.1):
@@ -3025,7 +3025,7 @@ packages:
     dependencies:
       '@babel/core': 7.9.0
       '@babel/helper-module-transforms': 7.19.0
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
       babel-plugin-dynamic-import-node: 2.3.3
     transitivePeerDependencies:
       - supports-color
@@ -3053,7 +3053,7 @@ packages:
     dependencies:
       '@babel/core': 7.9.0
       '@babel/helper-module-transforms': 7.19.0
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
       '@babel/helper-simple-access': 7.18.6
       babel-plugin-dynamic-import-node: 2.3.3
     transitivePeerDependencies:
@@ -3084,7 +3084,7 @@ packages:
       '@babel/core': 7.9.0
       '@babel/helper-hoist-variables': 7.18.6
       '@babel/helper-module-transforms': 7.19.0
-      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-plugin-utils': 7.19.0
       '@babel/helper-validator-identifier': 7.18.6
       babel-plugin-dynamic-import-node: 2.3.3
     transitivePeerDependencies:
@@ -4197,7 +4197,7 @@ packages:
       getenv: 1.0.0
       glob: 7.1.6
       resolve-from: 5.0.0
-      semver: 7.3.8
+      semver: 7.5.3
       slash: 3.0.0
       xcode: 3.0.1
       xml2js: 0.4.23
@@ -4219,7 +4219,7 @@ packages:
       getenv: 1.0.0
       glob: 7.1.6
       resolve-from: 5.0.0
-      semver: 7.3.8
+      semver: 7.5.3
       slash: 3.0.0
       xcode: 3.0.1
       xml2js: 0.4.23
@@ -5377,19 +5377,19 @@ packages:
       - debug
     dev: true
 
-  /@nrwl/tao@16.0.1:
-    resolution: {integrity: sha512-mvY+hKJwismbZY9WIfyO8wAOm1xtHn7ibUgiP/mazJv5OM3lnDoSfE83YlCVuhW+KgNq9OHQwIiAU+Y+hFQvEQ==}
+  /@nrwl/tao@16.5.0:
+    resolution: {integrity: sha512-lY/XV2n7iulHY77Uakt3Epa9m/NG7oTSN196baLjFykxUvLJI47PMX5qytugHkS8JLdcAB5p0qGsrQSHoi6jvg==}
     hasBin: true
     dependencies:
-      nx: 16.0.1
+      nx: 16.5.0
     transitivePeerDependencies:
       - '@swc-node/register'
       - '@swc/core'
       - debug
     dev: true
 
-  /@nx/nx-darwin-arm64@16.0.1:
-    resolution: {integrity: sha512-ezBykAa6etDIQdh0Rvl8AysJV7lU+/4Grxh0DYMWlVw9EsdJXh5ELr2NepnacdzML411cVSuo6m3SIT5NvMw2Q==}
+  /@nx/nx-darwin-arm64@16.5.0:
+    resolution: {integrity: sha512-0+5FH3ot5o0lpL0OKD4fO2n0a6LqLxr0LwU2VYxaAR1GLzOeVE5W3jBWY9ztOE+ktm8mGaZsdIIOQ77Iz/xwsQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [darwin]
@@ -5397,8 +5397,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-darwin-x64@16.0.1:
-    resolution: {integrity: sha512-ZTFVN0E+02EeWvToyKu1lQWdKHzPbks4R90Pw0cdDgnOaaYj2Z9GQ1rBo+3hq6K2xQb8ONOtsV3uWvrLKoQKww==}
+  /@nx/nx-darwin-x64@16.5.0:
+    resolution: {integrity: sha512-yziX2oXUSyOPOcRLmFMRsNs0eBVla5IGjAKqpY4OXAPBuyrOfgsW5ztj0PQM34gvqipXtTlN04Xt/U0jzQLudA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [darwin]
@@ -5406,8 +5406,17 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-linux-arm-gnueabihf@16.0.1:
-    resolution: {integrity: sha512-WmmVXv5HBNRzn/vWUYapJC5+CPUBaIXo6O8Iz+bniiHtsXeV5W5aYwveF1QOkHePLwUtkE18V1LDIkYksOEHOQ==}
+  /@nx/nx-freebsd-x64@16.5.0:
+    resolution: {integrity: sha512-hwIRRMyWrT2R4ozp6yXRNR1fwcclBlkkIQ51/1IzINPQxynMguuOvNZaJFD4OuZIDmI526++GmogPZc0aMzwkg==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@nx/nx-linux-arm-gnueabihf@16.5.0:
+    resolution: {integrity: sha512-BEWLpBhJ2AcZNDsiExLDcM9kmQ4+E+0YUcOsrAeX1s5D4HXXVtHMdTmOucKs4NNFqMuJ2Cf3ZzqmAIkRug0beA==}
     engines: {node: '>= 10'}
     cpu: [arm]
     os: [linux]
@@ -5415,8 +5424,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-linux-arm64-gnu@16.0.1:
-    resolution: {integrity: sha512-G8tOXwi5qmeBcwDah/FB7ge25Iimyu6jXuB+zL/IBXgE98Q0JtWJF/VRI5x3eo7xGq0XWH1V7msgIXN17/RNbQ==}
+  /@nx/nx-linux-arm64-gnu@16.5.0:
+    resolution: {integrity: sha512-EWmTbDLbBIjM/OJ594hoFKsEka/b8jM6NehL37mlIXL6fixUEA8LlO0MfUQ+kIPg79nWIujzulkIEhYFDWM1WA==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
@@ -5424,8 +5433,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-linux-arm64-musl@16.0.1:
-    resolution: {integrity: sha512-wokwHMtvTzisw8G9VHgKAfPLtPsKlkJqOXJJpHkbxKXDZvwf/AQs03xDWCSV2P7B277VIPWJxuZ7pFqne0nRfw==}
+  /@nx/nx-linux-arm64-musl@16.5.0:
+    resolution: {integrity: sha512-np/7+HEtEEvtu4zo3GBBPtTG8IP++vvH3o8VXpAB9eD4Jctz3rYzbfMc7GtLZkz8LCmCsjzqnrNtmcmoaRbomQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [linux]
@@ -5433,8 +5442,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-linux-x64-gnu@16.0.1:
-    resolution: {integrity: sha512-9GUs45JmqpGz14ZXt4636rxj6mul0UO9S6dJpJND7M3KPFbPELg0Urj9BUtXz4vhwL2lhKMcz6XvBpQSjyE2bg==}
+  /@nx/nx-linux-x64-gnu@16.5.0:
+    resolution: {integrity: sha512-iLOwgAaa1BHPLFhkBVi7GLAf6LfdYuv/R2rxlqq4d6fhv4Eq91Wo08LsqbFds+LpMN0CA+W/QMc3w9IIS/MPrA==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
@@ -5442,8 +5451,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-linux-x64-musl@16.0.1:
-    resolution: {integrity: sha512-vGq6e6/v+/YiKZeyKNyTmYNavOFTSHRAyze9I4UGB0aRouYrEuuemBSdCff+IXtwvUZEgYkEW5vTJainRBG+ig==}
+  /@nx/nx-linux-x64-musl@16.5.0:
+    resolution: {integrity: sha512-UE3tpgli7a08AsRaw/o1BUXnFOxICGzcYj1aglHBh6urVeUHK0aNt11djZcQ6ETgPgcjoGdwr7RqpANGnJQH9g==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [linux]
@@ -5451,8 +5460,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-win32-arm64-msvc@16.0.1:
-    resolution: {integrity: sha512-3xROoV57qq6ibzmeTNcx8j7Rdj3hmSryzge2ynh9Zr+4dFT4WrsF72WRYQg/kbgNRhV+IfDNq5SMM2mPDl+aRw==}
+  /@nx/nx-win32-arm64-msvc@16.5.0:
+    resolution: {integrity: sha512-u9cNKP8zrNIdeyaK5LHX+Zh+rkadE8tSE+vNulphCLhGuXJRpjaVY1juq9UQEo41NJQE6DuWWk2fnj4gALWugQ==}
     engines: {node: '>= 10'}
     cpu: [arm64]
     os: [win32]
@@ -5460,8 +5469,8 @@ packages:
     dev: true
     optional: true
 
-  /@nx/nx-win32-x64-msvc@16.0.1:
-    resolution: {integrity: sha512-7jkC3kukEBnV/1+YMFY0VjbGGlGihpQEj3LrgjVdM8+Uf1qcyKhmHlVV3iJ87Bix+wVG7TmsSJ+XPchh0FJlsw==}
+  /@nx/nx-win32-x64-msvc@16.5.0:
+    resolution: {integrity: sha512-E9109SAYNZXqCeWikZXyxNd7SZnCbdKGvqtQktS7dedHGwOmgIWfJ6bsvA7s2zHr09THQKJ4+7U1tDkWVNR9cg==}
     engines: {node: '>= 10'}
     cpu: [x64]
     os: [win32]
@@ -6344,7 +6353,7 @@ packages:
       eslint: 8.26.0
       ignore: 5.2.0
       regexpp: 3.2.0
-      semver: 7.3.8
+      semver: 7.5.3
       tsutils: 3.21.0(typescript@4.8.4)
       typescript: 4.8.4
     transitivePeerDependencies:
@@ -6370,7 +6379,7 @@ packages:
       eslint: 8.34.0
       ignore: 5.2.0
       regexpp: 3.2.0
-      semver: 7.3.8
+      semver: 7.5.3
       tsutils: 3.21.0(typescript@4.4.4)
       typescript: 4.4.4
     transitivePeerDependencies:
@@ -6550,7 +6559,7 @@ packages:
       debug: 4.3.4
       globby: 11.1.0
       is-glob: 4.0.3
-      semver: 7.3.8
+      semver: 7.5.3
       tsutils: 3.21.0(typescript@4.4.4)
       typescript: 4.4.4
     transitivePeerDependencies:
@@ -6571,7 +6580,7 @@ packages:
       debug: 4.3.4
       globby: 11.1.0
       is-glob: 4.0.3
-      semver: 7.3.8
+      semver: 7.5.3
       tsutils: 3.21.0(typescript@4.8.3)
       typescript: 4.8.3
     transitivePeerDependencies:
@@ -6592,7 +6601,7 @@ packages:
       debug: 4.3.4
       globby: 11.1.0
       is-glob: 4.0.3
-      semver: 7.3.8
+      semver: 7.5.3
       tsutils: 3.21.0(typescript@4.8.4)
       typescript: 4.8.4
     transitivePeerDependencies:
@@ -6613,7 +6622,7 @@ packages:
       eslint: 8.26.0
       eslint-scope: 5.1.1
       eslint-utils: 3.0.0(eslint@8.26.0)
-      semver: 7.3.8
+      semver: 7.5.3
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -6633,7 +6642,7 @@ packages:
       eslint: 8.34.0
       eslint-scope: 5.1.1
       eslint-utils: 3.0.0(eslint@8.34.0)
-      semver: 7.3.8
+      semver: 7.5.3
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -6653,7 +6662,7 @@ packages:
       eslint: 8.34.0
       eslint-scope: 5.1.1
       eslint-utils: 3.0.0(eslint@8.34.0)
-      semver: 7.3.8
+      semver: 7.5.3
     transitivePeerDependencies:
       - supports-color
       - typescript
@@ -7108,8 +7117,8 @@ packages:
     resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==}
     dev: true
 
-  /@yarnpkg/parsers@3.0.0-rc.40:
-    resolution: {integrity: sha512-sKbi5XhHKXCjzb5m0ftGuQuODM2iUXEsrCSl8MkKexNWHepCmU3IPaGTPC5gHZy4sOvsb9JqTLaZEez+kDzG+Q==}
+  /@yarnpkg/parsers@3.0.0-rc.46:
+    resolution: {integrity: sha512-aiATs7pSutzda/rq8fnuPwTglyVwjM22bNnK2ZgjrpAjQHSSl3lztd2f9evst1W/qnC58DRz7T7QndUDumAR4Q==}
     engines: {node: '>=14.15.0'}
     dependencies:
       js-yaml: 3.14.1
@@ -8460,7 +8469,7 @@ packages:
     dev: false
 
   /concat-map@0.0.1:
-    resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
 
   /concurrently@7.2.2:
     resolution: {integrity: sha512-DcQkI0ruil5BA/g7Xy3EWySGrFJovF5RYAYxwGvv9Jf9q9B1v3jPFP2tl6axExNf1qgF30kjoNYrangZ0ey4Aw==}
@@ -12043,7 +12052,7 @@ packages:
       jest-util: 27.5.1
       natural-compare: 1.4.0
       pretty-format: 27.5.1
-      semver: 7.3.8
+      semver: 7.5.3
     transitivePeerDependencies:
       - supports-color
     dev: true
@@ -13703,7 +13712,7 @@ packages:
     dependencies:
       hosted-git-info: 4.1.0
       is-core-module: 2.12.1
-      semver: 7.3.8
+      semver: 7.5.3
       validate-npm-package-license: 3.0.4
     dev: true
 
@@ -13769,8 +13778,8 @@ packages:
       - debug
     dev: true
 
-  /nx@16.0.1:
-    resolution: {integrity: sha512-x1NUs3ZGRWI3CdY1Qt4JdB9fOpjNkfP064HI3P6YjmHYSilbkVF7xe9bxOptjwCgMlTE3kOa66qC2fI5bKvG4w==}
+  /nx@16.5.0:
+    resolution: {integrity: sha512-X95atskaF1ejrF+C80mC4SwFPq0G/yFvxhfeWpPjKj7vUJEy1nZ4SjqlNVMORdN8dKQTE6ss76cIJux3fE7EXw==}
     hasBin: true
     requiresBuild: true
     peerDependencies:
@@ -13782,10 +13791,10 @@ packages:
       '@swc/core':
         optional: true
     dependencies:
-      '@nrwl/tao': 16.0.1
+      '@nrwl/tao': 16.5.0
       '@parcel/watcher': 2.0.4
       '@yarnpkg/lockfile': 1.1.0
-      '@yarnpkg/parsers': 3.0.0-rc.40
+      '@yarnpkg/parsers': 3.0.0-rc.46
       '@zkochan/js-yaml': 0.0.6
       axios: 1.3.4
       chalk: 4.1.2
@@ -13806,26 +13815,27 @@ packages:
       minimatch: 3.0.5
       npm-run-path: 4.0.1
       open: 8.4.0
-      semver: 7.3.4
+      semver: 7.5.3
       string-width: 4.2.3
       strong-log-transformer: 2.1.0
       tar-stream: 2.2.0
       tmp: 0.2.1
       tsconfig-paths: 4.1.2
-      tslib: 2.4.1
+      tslib: 2.6.0
       v8-compile-cache: 2.3.0
       yargs: 17.7.1
       yargs-parser: 21.1.1
     optionalDependencies:
-      '@nx/nx-darwin-arm64': 16.0.1
-      '@nx/nx-darwin-x64': 16.0.1
-      '@nx/nx-linux-arm-gnueabihf': 16.0.1
-      '@nx/nx-linux-arm64-gnu': 16.0.1
-      '@nx/nx-linux-arm64-musl': 16.0.1
-      '@nx/nx-linux-x64-gnu': 16.0.1
-      '@nx/nx-linux-x64-musl': 16.0.1
-      '@nx/nx-win32-arm64-msvc': 16.0.1
-      '@nx/nx-win32-x64-msvc': 16.0.1
+      '@nx/nx-darwin-arm64': 16.5.0
+      '@nx/nx-darwin-x64': 16.5.0
+      '@nx/nx-freebsd-x64': 16.5.0
+      '@nx/nx-linux-arm-gnueabihf': 16.5.0
+      '@nx/nx-linux-arm64-gnu': 16.5.0
+      '@nx/nx-linux-arm64-musl': 16.5.0
+      '@nx/nx-linux-x64-gnu': 16.5.0
+      '@nx/nx-linux-x64-musl': 16.5.0
+      '@nx/nx-win32-arm64-msvc': 16.5.0
+      '@nx/nx-win32-x64-msvc': 16.5.0
     transitivePeerDependencies:
       - debug
     dev: true
@@ -15512,16 +15522,16 @@ packages:
     engines: {node: '>=10'}
     hasBin: true
 
-  /semver@7.3.4:
-    resolution: {integrity: sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==}
+  /semver@7.3.8:
+    resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:
       lru-cache: 6.0.0
     dev: true
 
-  /semver@7.3.8:
-    resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
+  /semver@7.5.3:
+    resolution: {integrity: sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==}
     engines: {node: '>=10'}
     hasBin: true
     dependencies:


Test patch:
diff --git a/packages/query-core/src/tests/focusManager.test.tsx b/packages/query-core/src/tests/focusManager.test.tsx
index 645a93f47..6e23df625 100644
--- a/packages/query-core/src/tests/focusManager.test.tsx
+++ b/packages/query-core/src/tests/focusManager.test.tsx
@@ -38,20 +38,6 @@ describe('focusManager', () => {
     expect(focusManager.isFocused()).toBeTruthy()
   })
 
-  it('should not notify listeners on focus if already focused', async () => {
-    const subscriptionSpy = jest.fn()
-    const unsubscribe = focusManager.subscribe(subscriptionSpy)
-
-    focusManager.setFocused(true)
-    expect(subscriptionSpy).toHaveBeenCalledTimes(1)
-    subscriptionSpy.mockReset()
-
-    focusManager.setFocused(false)
-    expect(subscriptionSpy).toHaveBeenCalledTimes(0)
-
-    unsubscribe()
-  })
-
   it('should return true for isFocused if document is undefined', async () => {
     const { document } = globalThis
 
@@ -152,4 +138,25 @@ describe('focusManager', () => {
 
     unsubscribe2()
   })
+
+  test('should call listeners when setFocused is called', () => {
+    const listener = jest.fn()
+
+    focusManager.subscribe(listener)
+
+    focusManager.setFocused(true)
+    focusManager.setFocused(true)
+
+    expect(listener).toHaveBeenCalledTimes(1)
+
+    focusManager.setFocused(false)
+    focusManager.setFocused(false)
+
+    expect(listener).toHaveBeenCalledTimes(2)
+
+    focusManager.setFocused(undefined)
+    focusManager.setFocused(undefined)
+
+    expect(listener).toHaveBeenCalledTimes(3)
+  })
 })
diff --git a/packages/query-core/src/tests/onlineManager.test.tsx b/packages/query-core/src/tests/onlineManager.test.tsx
index c3a00e86d..ab35a7bbf 100644
--- a/packages/query-core/src/tests/onlineManager.test.tsx
+++ b/packages/query-core/src/tests/onlineManager.test.tsx
@@ -145,4 +145,25 @@ describe('onlineManager', () => {
 
     unsubscribe2()
   })
+
+  test('should call listeners when setOnline is called', () => {
+    const listener = jest.fn()
+
+    onlineManager.subscribe(listener)
+
+    onlineManager.setOnline(true)
+    onlineManager.setOnline(true)
+
+    expect(listener).toHaveBeenCalledTimes(1)
+
+    onlineManager.setOnline(false)
+    onlineManager.setOnline(false)
+
+    expect(listener).toHaveBeenCalledTimes(2)
+
+    onlineManager.setOnline(undefined)
+    onlineManager.setOnline(undefined)
+
+    expect(listener).toHaveBeenCalledTimes(3)
+  })
 })
diff --git a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx
index 3aa840221..bce325756 100644
--- a/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx
+++ b/packages/react-query-persist-client/src/__tests__/PersistQueryClientProvider.test.tsx
@@ -325,7 +325,7 @@ describe('PersistQueryClientProvider', () => {
     await waitFor(() => rendered.getByText('data: null'))
     await waitFor(() => rendered.getByText('data: hydrated'))
 
-    expect(states).toHaveLength(3)
+    expect(states).toHaveLength(2)
 
     expect(fetched).toBe(false)
 
@@ -340,9 +340,6 @@ describe('PersistQueryClientProvider', () => {
       fetchStatus: 'idle',
       data: 'hydrated',
     })
-
-    // #5443 seems like we get an extra render now ...
-    expect(states[1]).toStrictEqual(states[2])
   })
 
   test('should call onSuccess after successful restoring', async () => {
diff --git a/packages/react-query/src/__tests__/useQuery.test.tsx b/packages/react-query/src/__tests__/useQuery.test.tsx
index 2fb62e5b4..b5550d885 100644
--- a/packages/react-query/src/__tests__/useQuery.test.tsx
+++ b/packages/react-query/src/__tests__/useQuery.test.tsx
@@ -914,17 +914,15 @@ describe('useQuery', () => {
     // required to make sure no additional renders are happening after data is successfully fetched for the second time
     await sleep(100)
 
-    expect(states.length).toBe(5)
+    expect(states.length).toBe(4)
     // First load
     expect(states[0]).toMatchObject({ isLoading: true, isSuccess: false })
     // First success
     expect(states[1]).toMatchObject({ isLoading: false, isSuccess: true })
     // Remove
     expect(states[2]).toMatchObject({ isLoading: true, isSuccess: false })
-    // Hook state update
-    expect(states[3]).toMatchObject({ isLoading: true, isSuccess: false })
     // Second success
-    expect(states[4]).toMatchObject({ isLoading: false, isSuccess: true })
+    expect(states[3]).toMatchObject({ isLoading: false, isSuccess: true })
   })
 
   it('should fetch when refetchOnMount is false and nothing has been fetched yet', async () => {
@@ -3650,6 +3648,7 @@ describe('useQuery', () => {
           )
           act(() => setPrefetched(true))
         }
+
         prefetch()
       }, [])
 
@@ -5879,6 +5878,7 @@ describe('useQuery', () => {
         </div>
       )
     }
+
     const rendered = renderWithClient(queryClient, <Page />)
     const fetchBtn = rendered.getByRole('button', { name: 'refetch' })
     await waitFor(() => rendered.getByText('data: 1'))
@@ -5916,8 +5916,132 @@ describe('useQuery', () => {
         </div>
       )
     }
+
     const rendered = renderWithClient(queryClient, <Page />)
     await waitFor(() => rendered.getByText('status: success'))
     await waitFor(() => rendered.getByText('data: 1'))
   })
+  it('should reuse same data object reference when queryKey changes back to some cached data', async () => {
+    const spy = jest.fn()
+    const key = queryKey()
+
+    async function fetchNumber(id: number) {
+      await sleep(5)
+      return { numbers: { current: { id } } }
+    }
+    function Test() {
+      const [id, setId] = React.useState(1)
+
+      const { data } = useQuery({
+        select: selector,
+        queryKey: [key, 'user', id],
+        queryFn: () => fetchNumber(id),
+      })
+
+      React.useEffect(() => {
+        spy(data)
+      }, [data])
+
+      return (
+        <div>
+          <button name="1" onClick={() => setId(1)}>
+            1
+          </button>
+          <button name="2" onClick={() => setId(2)}>
+            2
+          </button>
+          <span>Rendered Id: {data?.id}</span>
+        </div>
+      )
+    }
+
+    function selector(data: any) {
+      return data.numbers.current
+    }
+
+    const rendered = renderWithClient(queryClient, <Test />)
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    await waitFor(() => rendered.getByText('Rendered Id: 1'))
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /2/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 2'))
+    expect(spy).toHaveBeenCalledTimes(2) // called with undefined because id changed
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /1/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 1'))
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /2/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 2'))
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
+  it('should reuse same data object reference when queryKey changes and placeholderData is present', async () => {
+    const spy = jest.fn()
+    const key = queryKey()
+
+    async function fetchNumber(id: number) {
+      await sleep(5)
+      return { numbers: { current: { id } } }
+    }
+    function Test() {
+      const [id, setId] = React.useState(1)
+
+      const { data } = useQuery({
+        select: selector,
+        queryKey: [key, 'user', id],
+        queryFn: () => fetchNumber(id),
+        placeholderData: { numbers: { current: { id: 99 } } },
+      })
+
+      React.useEffect(() => {
+        spy(data)
+      }, [data])
+
+      return (
+        <div>
+          <button name="1" onClick={() => setId(1)}>
+            1
+          </button>
+          <button name="2" onClick={() => setId(2)}>
+            2
+          </button>
+          <span>Rendered Id: {data?.id}</span>
+        </div>
+      )
+    }
+
+    function selector(data: any) {
+      return data.numbers.current
+    }
+
+    const rendered = renderWithClient(queryClient, <Test />)
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    await waitFor(() => rendered.getByText('Rendered Id: 99'))
+    await waitFor(() => rendered.getByText('Rendered Id: 1'))
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /2/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 99'))
+    await waitFor(() => rendered.getByText('Rendered Id: 2'))
+    expect(spy).toHaveBeenCalledTimes(2) // called with undefined because id changed
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /1/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 1'))
+    expect(spy).toHaveBeenCalledTimes(1)
+
+    spy.mockClear()
+    fireEvent.click(rendered.getByRole('button', { name: /2/ }))
+    await waitFor(() => rendered.getByText('Rendered Id: 2'))
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
 })