Repository: urql-graphql/urql
Pull request #3560 (2024-04-10 09:07:36)

fix(graphcache): ignore current entity during invalidateType
## Summary

After a conversation in Discord we discovered that an old "bug" is biting us with the new default functionality. The old bug goes as follows: when we invalidate in a mutation-updater and the invalidated entity belongs to the mutation response we effectively make the mutation response go to null due to us re-querying the data after our write, the re-querying happens so we respect `resolvers` for our mutation data.

This is an ad-hoc fix to reduce the impact of this change, we effectively exclude the entities we find in the mutation response so we can return a good response. In the future we might be able to establish a similar principle for custom invalidations/updates as well.


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

Latest commit: 9079fcafe41221d16290760045dc94af18200702

**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/ignore-current-entity?filename=.changeset/selfish-ants-mix.md&value=---%0A%22%40urql%2Fexchange-graphcache%22%3A%20patch%0A---%0A%0Afix(graphcache)%3A%20ignore%20current%20entity%20during%20invalidateType%0A)



Patch:
diff --git a/.changeset/rich-suns-sit.md b/.changeset/rich-suns-sit.md
new file mode 100644
index 00000000..4e0b9c5e
--- /dev/null
+++ b/.changeset/rich-suns-sit.md
@@ -0,0 +1,5 @@
+---
+'@urql/exchange-graphcache': patch
+---
+
+When invoking the automatic creation updater ignore the entity we are currently on in the mutation
diff --git a/exchanges/graphcache/src/operations/invalidate.ts b/exchanges/graphcache/src/operations/invalidate.ts
index 02497b12..bf739743 100644
--- a/exchanges/graphcache/src/operations/invalidate.ts
+++ b/exchanges/graphcache/src/operations/invalidate.ts
@@ -25,9 +25,13 @@ export const invalidateEntity = (
   }
 };
 
-export const invalidateType = (typename: string) => {
+export const invalidateType = (
+  typename: string,
+  excludedEntities: string[]
+) => {
   const types = InMemoryData.getEntitiesForType(typename);
   for (const entity of types) {
+    if (excludedEntities.includes(entity)) continue;
     invalidateEntity(entity);
   }
 };
diff --git a/exchanges/graphcache/src/operations/write.ts b/exchanges/graphcache/src/operations/write.ts
index 7d52a75a..56b7af62 100644
--- a/exchanges/graphcache/src/operations/write.ts
+++ b/exchanges/graphcache/src/operations/write.ts
@@ -383,13 +383,16 @@ const writeSelection = (
       // if we don't we'll assume this is a create mutation and invalidate
       // the found __typename.
       if (fieldValue && Array.isArray(fieldValue)) {
+        const excludedEntities: string[] = fieldValue.map(
+          entity => ctx.store.keyOfEntity(entity) || ''
+        );
         for (let i = 0, l = fieldValue.length; i < l; i++) {
-          const key = ctx.store.keyOfEntity(fieldValue[i]);
+          const key = excludedEntities[i];
           if (key && fieldValue[i].__typename) {
             const resolved = InMemoryData.readRecord(key, '__typename');
             const count = InMemoryData!.getRefCount(key);
             if (resolved && !count) {
-              invalidateType(fieldValue[i].__typename);
+              invalidateType(fieldValue[i].__typename, excludedEntities);
             }
           }
         }
@@ -399,7 +402,7 @@ const writeSelection = (
           const resolved = InMemoryData.readRecord(key, '__typename');
           const count = InMemoryData.getRefCount(key);
           if ((!resolved || !count) && fieldValue.__typename) {
-            invalidateType(fieldValue.__typename);
+            invalidateType(fieldValue.__typename, [key]);
           }
         }
       }
diff --git a/exchanges/graphcache/src/store/store.ts b/exchanges/graphcache/src/store/store.ts
index a6538f79..c528e833 100644
--- a/exchanges/graphcache/src/store/store.ts
+++ b/exchanges/graphcache/src/store/store.ts
@@ -174,7 +174,7 @@ export class Store<
       !this.resolve(entity, '__typename');
 
     if (shouldInvalidateType) {
-      invalidateType(entity);
+      invalidateType(entity, []);
     } else {
       invariant(
         entityKey,


Test patch:
diff --git a/exchanges/graphcache/src/cacheExchange.test.ts b/exchanges/graphcache/src/cacheExchange.test.ts
index 73cbedfb..1ba13805 100644
--- a/exchanges/graphcache/src/cacheExchange.test.ts
+++ b/exchanges/graphcache/src/cacheExchange.test.ts
@@ -1927,6 +1927,12 @@ describe('mutation updates', () => {
     vi.runAllTimers();
     expect(response).toHaveBeenCalledTimes(3);
     expect(result).toHaveBeenCalledTimes(3);
+    expect(result.mock.calls[1][0].data).toEqual({
+      addAuthor: {
+        id: '2',
+        name: 'Author 2',
+      },
+    });
   });
 });