Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The refetchQueries option of useMutation refetches the query every time the cache changes #8586

Closed
hevele-moda opened this issue Aug 4, 2021 · 1 comment · Fixed by #8588

Comments

@hevele-moda
Copy link

Intended outcome:
Using a useMutation hook with the refetchQueries option refetches these queries after the mutation once, but doesn't affect anything later.

Actual outcome:
The queries from refetchQueries are refetched every time when some data in the cache changes in a way that affects these queries.

How to reproduce the issue:

  • clone https://github.com/hevele-moda/react-apollo-error-template
  • run npm install && npm run start
  • open the console where I logged the operationName for every GraphQL operation invoked
  • remove a person and notice how AllPeople is refetched, which is correct
  • now add a new person and notice how AllPeople is refetched again, when it shouldn't be
  • remove another person and notice that now AllPeople is refetched twice, when it should be refetched only once

Versions

System:
  OS: Linux 5.4 Ubuntu 20.04.2 LTS (Focal Fossa)
Binaries:
  Node: 14.17.1 - ~/.nvm/versions/node/v14.17.1/bin/node
  Yarn: 1.21.1 - /usr/bin/yarn
  npm: 6.14.13 - ~/.nvm/versions/node/v14.17.1/bin/npm
Browsers:
  Chrome: 92.0.4515.107
  Firefox: 90.0.2
npmPackages:
  @apollo/client: ^3.4.4 => 3.4.4 
@benjamn benjamn self-assigned this Aug 4, 2021
benjamn added a commit that referenced this issue Aug 4, 2021
This invariant is expected to fail, and serves as a minimal regression
test for issue #8586.
benjamn added a commit that referenced this issue Aug 4, 2021
This refactoring ensures `observableQuery.queryId` ends up the same as
`observableQuery.queryInfo.queryId`, which is relevant in cases when we
choose a specific query ID string before creating the `QueryInfo` and
`ObservableQuery` objects.

Ensuring that `queryInfo.queryId === queryInfo.observableQuery.queryId`
(another way of saying the same thing) also fixes the `invariant`
failures I deliberately introduced in the previous commit.

This inconsistency between IDs was the cause of issue #8586, a double
registration bug for mutation `refetchQueries` specified using the
legacy one-time `refetchQueries: [{ query, variables }]` style.

Another (recommended) way to work around that problem would be to
specify `refetchQueries: [query]` instead, to select the existing query
by its `DocumentNode` rather than creating and deleting a new one-time
legacy query.
benjamn added a commit that referenced this issue Aug 4, 2021
@benjamn
Copy link
Member

benjamn commented Aug 4, 2021

@hevele-moda Thanks for the reproduction! Fix incoming: #8588

In case you want to re-test your reproduction yourself, I found I needed to make a few tweaks to make everything work:

diff --git a/src/index.jsx b/src/index.jsx
index 9bbd88c..9cb1a4b 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -53,8 +53,9 @@ const MutationType = new GraphQLObjectType({
       args: {
         id: { type: GraphQLID },
       },
-      resolve: function (_, { id }) {
-        peopleData.splice(peopleData.findIndex(person => person.id === id), 1);
+      resolve(_, { id }) {
+        const index = peopleData.findIndex(person => person.id == id);
+        if (index >= 0) peopleData.splice(index, 1);
         return peopleData;
       }
     }
@@ -188,7 +193,9 @@ function App() {
         <ul>
           {data?.people.map(person => (
             <li key={person.id}>
-              {person.name} <button onClick={() => removePerson(person.id)}>Delete</button>
+              {person.name} <button onClick={() => {
+                removePerson({ variables: { id: person.id }});
+              }}>Delete</button>
             </li>
           ))}
         </ul>

Though it's important to us to fix regressions like these, so your code will continue to work without modification, I would also strongly recommend switching to the following style for refetchQueries:

diff --git a/src/index.jsx b/src/index.jsx
index 9bbd88c..9cb1a4b 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -156,7 +157,11 @@ function App() {
     },
   });
 
-  const [removePerson] = useMutation(REMOVE_PERSON, {refetchQueries: [{query: ALL_PEOPLE}]});
+  const [removePerson] = useMutation(REMOVE_PERSON, {
+    refetchQueries: [ALL_PEOPLE],
+  });
 
   return (
     <main>

Since there's an existing query you want to refetch, you can identify it by passing its DocumentNode in the mutation's refetchQueries: [ALL_PEOPLE] array, rather than creating a whole new query, only to discard it after refetching it once (which is what the refetchQueries: [{ query }] style unfortunately does).

The bug solved by #8588 has to do with the cleanup of these one-shot queries, so you can avoid the bug by avoiding one-shot queries, as a workaround for your original issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.