Repository: urql-graphql/urql
Pull request #3497 (2024-02-02 14:24:06)

fix(graphcache): Fix `@defer` state carrying over to next operation
Related to #3167

## Summary

This fixes the `@defer` states becoming “sticky” and carrying over to subsequent
operations sometimes, causing incomplete results to be generated under certain conditions.

## Set of changes

- Forcefully reset `deferRef` when a new operation is started


changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: d182f628f0381d1856f41ae21b7c9e10394d1558

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                      | Type  |
| ------------------------- | ----- |
| @urql/exchange-graphcache | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/urql-graphql/urql/new/fix/defer-ref-reset?filename=.changeset/nine-geese-greet.md&value=---%0A%22%40urql%2Fexchange-graphcache%22%3A%20patch%0A---%0A%0Afix(graphcache)%3A%20Fix%20%60%40defer%60%20state%20carrying%20over%20to%20next%20operation%0A)



Patch:
diff --git a/.changeset/serious-tables-ring.md b/.changeset/serious-tables-ring.md
new file mode 100644
index 00000000..7dfc7a8b
--- /dev/null
+++ b/.changeset/serious-tables-ring.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+Fix `@defer` state leaking into following operations.
diff --git a/exchanges/graphcache/src/operations/shared.ts b/exchanges/graphcache/src/operations/shared.ts
index 3c3d1b6a..458e599f 100644
--- a/exchanges/graphcache/src/operations/shared.ts
+++ b/exchanges/graphcache/src/operations/shared.ts
@@ -64,6 +64,8 @@ export const makeContext = (
   entityKey: string,
   error: CombinedError | undefined
 ): Context => {
+  deferRef = false;
+
   const ctx: Context = {
     store,
     variables,


Test patch:
diff --git a/exchanges/graphcache/src/cacheExchange.test.ts b/exchanges/graphcache/src/cacheExchange.test.ts
index 170b2d5f..9b4eff71 100644
--- a/exchanges/graphcache/src/cacheExchange.test.ts
+++ b/exchanges/graphcache/src/cacheExchange.test.ts
@@ -3007,4 +3007,75 @@ describe('commutativity', () => {
     expect(combinedData).toHaveProperty('data.deferred.id', 1);
     expect(combinedData).toHaveProperty('data.node.id', 2);
   });
+
+  it('applies deferred logic only to deferred operations', () => {
+    let failingData: OperationResult | undefined;
+
+    const client = createClient({
+      url: 'http://0.0.0.0',
+      exchanges: [],
+    });
+
+    const { source: ops$, next: nextOp } = makeSubject<Operation>();
+    const { source: res$ } = makeSubject<OperationResult>();
+
+    const deferredQuery = gql`
+      {
+        ... @defer {
+          deferred {
+            id
+            name
+          }
+        }
+      }
+    `;
+
+    const failingQuery = gql`
+      {
+        deferred {
+          id
+          name
+        }
+      }
+    `;
+
+    const forward = (ops$: Source<Operation>): Source<OperationResult> =>
+      share(
+        merge([
+          pipe(
+            ops$,
+            filter(() => false)
+          ) as any,
+          res$,
+        ])
+      );
+
+    pipe(
+      cacheExchange()({ forward, client, dispatchDebug })(ops$),
+      tap(result => {
+        if (result.operation.kind === 'query') {
+          if (result.operation.key === 1) {
+            failingData = result;
+          }
+        }
+      }),
+      publish
+    );
+
+    const failingOp = client.createRequestOperation('query', {
+      key: 1,
+      query: failingQuery,
+      variables: undefined,
+    });
+    const deferredOp = client.createRequestOperation('query', {
+      key: 2,
+      query: deferredQuery,
+      variables: undefined,
+    });
+
+    nextOp(deferredOp);
+    nextOp(failingOp);
+
+    expect(failingData).not.toMatchObject({ hasNext: true });
+  });
 });