From abfd02abeb8585e44377e9e87e5d20e5d95be002 Mon Sep 17 00:00:00 2001
From: Sungyu Kang <gron1gh1@gmail.com>
Date: Thu, 18 Jan 2024 04:59:00 +0900
Subject: [PATCH] Fix `networkStatus` update issue in `useSuspenseQuery` with
 `cache-and-network` policy (#11489)

---
 .changeset/soft-roses-sit.md                  |  5 ++
 .size-limits.json                             |  4 +-
 src/react/cache/QueryReference.ts             |  5 +-
 .../hooks/__tests__/useSuspenseQuery.test.tsx | 47 +++++++++++++++++++
 4 files changed, 58 insertions(+), 3 deletions(-)
 create mode 100644 .changeset/soft-roses-sit.md

diff --git a/.changeset/soft-roses-sit.md b/.changeset/soft-roses-sit.md
new file mode 100644
index 00000000000..0d01c462e1c
--- /dev/null
+++ b/.changeset/soft-roses-sit.md
@@ -0,0 +1,5 @@
+---
+"@apollo/client": patch
+---
+
+Fix `networkStatus` with `useSuspenseQuery` not properly updating to ready state when using a `cache-and-network` fetch policy that returns data equal to what is already in the cache.
diff --git a/.size-limits.json b/.size-limits.json
index f6dfadb64ac..395aea47c38 100644
--- a/.size-limits.json
+++ b/.size-limits.json
@@ -1,4 +1,4 @@
 {
-  "dist/apollo-client.min.cjs": 37922,
-  "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 31974
+  "dist/apollo-client.min.cjs": 37930,
+  "import { ApolloClient, InMemoryCache, HttpLink } from \"dist/index.js\" (production)": 31972
 }
diff --git a/src/react/cache/QueryReference.ts b/src/react/cache/QueryReference.ts
index 5b2fc14c643..a238fc1827f 100644
--- a/src/react/cache/QueryReference.ts
+++ b/src/react/cache/QueryReference.ts
@@ -238,7 +238,10 @@ export class InternalQueryReference<TData = unknown> {
         // This occurs when switching to a result that is fully cached when this
         // class is instantiated. ObservableQuery will run reobserve when
         // subscribing, which delivers a result from the cache.
-        if (result.data === this.result.data) {
+        if (
+          result.data === this.result.data &&
+          result.networkStatus === this.result.networkStatus
+        ) {
           return;
         }
 
diff --git a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx
index 20f3a83b668..e762e258fa8 100644
--- a/src/react/hooks/__tests__/useSuspenseQuery.test.tsx
+++ b/src/react/hooks/__tests__/useSuspenseQuery.test.tsx
@@ -9932,6 +9932,53 @@ describe("useSuspenseQuery", () => {
     });
   });
 
+  it("updates networkStatus when a network request returns the same cached data with 'cache-and-network' fetchPolicy", async () => {
+    const { query } = useSimpleQueryCase();
+
+    const link = new ApolloLink(() => {
+      return new Observable((observer) => {
+        setTimeout(() => {
+          observer.next({ data: { greeting: "Hello" } });
+          observer.complete();
+        }, 10);
+      });
+    });
+
+    const client = new ApolloClient({
+      link,
+      cache: new InMemoryCache(),
+    });
+
+    // preloaded cache
+    await client.writeQuery({ query, data: { greeting: "Hello" } });
+
+    const { result } = renderSuspenseHook(
+      () =>
+        useSuspenseQuery(query, {
+          fetchPolicy: "cache-and-network",
+        }),
+      { client }
+    );
+
+    await waitFor(() => {
+      // We should see the cached greeting while the network request is in flight
+      // and the network status should be set to `loading`.
+      expect(result.current).toMatchObject({
+        data: { greeting: "Hello" },
+        networkStatus: NetworkStatus.loading,
+      });
+    });
+
+    await waitFor(() => {
+      // We should see the updated greeting once the network request finishes
+      // and the network status should be set to `ready`.
+      expect(result.current).toMatchObject({
+        data: { greeting: "Hello" },
+        networkStatus: NetworkStatus.ready,
+      });
+    });
+  });
+
   describe.skip("type tests", () => {
     it("returns unknown when TData cannot be inferred", () => {
       const query = gql`