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

Apollo Client 3 reloading query after mutation update #6760

Closed
danfernand opened this issue Aug 2, 2020 · 42 comments · Fixed by #10714
Closed

Apollo Client 3 reloading query after mutation update #6760

danfernand opened this issue Aug 2, 2020 · 42 comments · Fixed by #10714

Comments

@danfernand
Copy link

Hello Apollo team. We have an existing app that we are migrating from Apollo Client 2 to 3. We noticed an issue that is affecting our upgrade. After an apollo mutation modifying an entity that previously a query has loaded we are noticing we are setting loading to true if the fetch policy is set to cache-and-network.

We use cache-and-network to guarantee that our app is up to date with the backend and expect the cache to be updated with the network silently.

I am not sure if the behavior is expected since its different than AC 2.

Below I have created 2 code sandboxes that are updating a graphql server created with FaunaDB to show the issue.

Apollo Client 3 - https://codesandbox.io/s/funny-https-zb1hf

Apollo Client 2 - https://codesandbox.io/s/ecstatic-cori-t42ce

When updating completed by selecting the opposite value from a todo the whole page is reloading due to using loading from the query to show a loading state.

Both codesanbox examples are using the same graphql server.

I looking all over to confirm that this AC3 behavior is expected but cannot find anything to that regard.

Thank you for your time.

PS. Great job on the ecosystem! Once we get past this its gonna be awesome!

@hauxir
Copy link

hauxir commented Aug 2, 2020

experiencing the same issue

@kure-
Copy link

kure- commented Aug 3, 2020

I'm experienced something similar, that might be connected to your behaviour. It feels like an errorneous behaviour of the cache. Your mutation is returning data on the response of the request. The query fetchPolicy is set to cache-and-network. The response of the request is automatically taken to write the data to cache and after that write a refetech is triggered and it should not happen. Rather than that, the cache should be updated and the component just rerender with fresh data and loading state is not set to true.

The core problem is probably the same in my situation. My mutation is not responding with data. Instead of that, I'm just using simple refetchQueries with an array of two query names. The problem here is, that those two queries contain some of the refs of other queries, and those other queries are not part of the refetch array. Those other queries are called within mounted components with queries with fetchPolicy also set to cache-and-network.
So what was working in version 2.6.something is, that mutation was called, after it was completed, two network requests have been called, cache has been updated and components were rerendered. The other components, that were using some of the refs of the two queries that were refeteched, were just rerendered with fresh data. No other network requests were made.
The main problem is, that right now, instead of 2 queries that should be refetched, i'm having like 5 or more network requests triggered. Some of them even twice :)

@benjamn benjamn added this to the Post 3.0 milestone Aug 3, 2020
@benjamn
Copy link
Member

benjamn commented Aug 3, 2020

@danfernand One of the things that changed in AC3 is that cache-and-network really means to read from the cache and make a network request, not just the first time, but whenever there's a cache update that affects this query.

Although I have a specific recommendation to achieve your desired behavior (verified using your reproduction—thanks!), there's a bit of history here that I think is worth understanding. PR #6221 was a big refactoring (released in -beta.46) that made FetchPolicy enforcement more consistent, and restored the -and-network part of cache-and-network, as described above. Realizing that this might not be the behavior folks actually want, we implemented (what we thought was) a clever trick to fall back to cache-first after the first network request, in PR #6353. This behavior was still in place when we released 3.0.0, but post-launch feedback convinced us to revert that PR in #6712, because modifying the options.fetchPolicy that you requested can be surprising and is not universally appropriate. In other words, your reproduction is making unwanted network requests because cache-and-network is being enforced literally.

However, PR #6712 did more than just revert #6353, because cache-and-network-followed-by-cache-first still seems like a very common hybrid policy that people may want to implement. To make that behavior easier to achieve, we also introduced options.nextFetchPolicy (available in @apollo/[email protected]), so you can specify an initial fetch policy (via options.fetchPolicy, as before) and also request a different fetch policy next time.

Long story short, if you use options.nextFetchPolicy as follows, I believe you can get the behavior you're looking for:

export default function Todos() {
  const { loading, error, data } = useQuery(GET_ALL_TODOS, {
    fetchPolicy: "cache-and-network",
    nextFetchPolicy: "cache-first",
    variables: {
      _size: 10
    }
  });
  ...
}

This means the very first query will read from the cache and make a network request, but subsequent queries will make a network request only if the cache data has become incomplete.

@danfernand
Copy link
Author

Thank you for your response! Will give nextFetchPolicy a whirl! Apollo has made using graphql amazing. Great job!

@kure-
Copy link

kure- commented Aug 4, 2020

thanks @benjamn , adding nextFetchPolicy seems to be working just well. The behaviour seems to be quite the same as in Apollo version 2 with fetchPolicy: cache-and-network 👍

@twittwer
Copy link

I added the nextFetchPolicy: "cache-first" to a watchQuery.
This watchQuery was updated by a mutation with optimisticResponse & update function.
After adding the nextFetchPolicy the eatchQuery doesn't emit the optimistic change anymore.

Did someone experience something similar or can point me in the right direction?

@hinok
Copy link

hinok commented Aug 21, 2020

@benjamn Can you confirm that such behaviour is expected?

Let's say that I have two separate components that run two queries like below:

ComponentA

query A {
  user {
    id
    role
  }
}

ComponentB

query B {
  user {
    id
    role
    name
  }
  products {
    // a lot of fields...
  }
}

When I render ComponentA and run query A, after getting the response user { id, role }, apollo will call also query B because it contains user { id, role }.

AC3 is that cache-and-network really means to read from the cache and make a network request, not just the first time, but whenever there's a cache update that affects this query.

So by a cache update, you meant anything that does something with the cache, right? Running queries, mutations or manually modifying cache by cache.writeFragment, cache.modify, right?

@jperl
Copy link

jperl commented Sep 2, 2020

We want variable changes to trigger refetch from network, but not changes to the subtree causes by other mutations. With this example, if the mySizeProp changes will it refetch from the network?

  const { loading, error, data } = useQuery(GET_ALL_TODOS, {
    fetchPolicy: "cache-and-network",
    nextFetchPolicy: "cache-first",
    variables: {
      _size: mySizeProp
    }
  });

@edbond88
Copy link

edbond88 commented Sep 4, 2020

@benjamn I think it would be nice to add this example to the documentation in the section about the update

@tim-field
Copy link

tim-field commented Sep 5, 2020

This should really be part of the migration guide 🙏 Went down a lot of rabbit holes to find this issue! https://www.apollographql.com/docs/react/migrating/apollo-client-3-migration/

@twittwer
Copy link

twittwer commented Sep 7, 2020

I added the nextFetchPolicy: "cache-first" to a watchQuery.
This watchQuery was updated by a mutation with optimisticResponse & update function.
After adding the nextFetchPolicy the eatchQuery doesn't emit the optimistic change anymore.

Did someone experience something similar or can point me in the right direction?

Can someone say anything about how to handle optimsitic responses?

@danfernand
Copy link
Author

We have optimistic responses in our apps and they did not change at all. Two things that I can think of is optimistic payload is incorrect and thus cache is not being updated. There should be an error on the console for this. Another thing is that Apollo client 3 cache cannot cache object if there is no _id or id fields. There would be a console info for this!

@twittwer
Copy link

twittwer commented Sep 8, 2020

@danfernand The optimistic responses are not changing. The problem I tried to explain is about the active watchQueries. They are not updated after a mutation with optimisticResponse+updateFunction. The watchQuery with fetchPolicy: "cache-and-network" gets updated, but as soon as I add nextFetchPolicy: "cache-first" it doesn't get any update from optimistic mutations.

@esseswann
Copy link

esseswann commented Sep 14, 2020

I have the following situation:

query A {
  rootField {
     field {
        subfield {
          ...
       }
    }
  }
}

where subfield is an array
Mutation that returns none of the rootField or filed or subfield
Update function that removes items from subfield array using writeFragment to field

When I call writeFragment the query refetches. I added cache-first and it still refetches. I tried using cache-only but the { data } prop becomes empty, I suppose because Apollo thinks that the cache is incomplete

I would like to only modify the subfield array in the field without causing any extra requests

@laij84
Copy link

laij84 commented Sep 25, 2020

I just upgraded from 3.0.2 to 3.2.1 and am experiencing the same issue, for both cache-and-network and network-only fetch policies. In 3.0.2 this issue was not present.

@mpgon
Copy link

mpgon commented Sep 8, 2021

I am having a problem related to what @sinn1 mentioned above

At first nextFetchPolicy: catch-first didn't work for me but I found that it was because the fields returned from the mutation didn't match the fields that were specified in the query.

This seems like a breaking change not mentioned anywhere? Or maybe I'm missing something. But when upgrading to v3, I have a lot of queries being refetched 2, 3, 4 times because a subsequent query fetches only a subset of the data. But I'm fetching data subsets everywhere. Example:

useGetAccount
{
  id
  first_name
  last_name
}
useGetItem
{
  id
  account {
    id
    first_name
  }
}

useGetItem triggers useGetAccount to get fetched again, with "cache-first". only way to solve it is using ""cache-first" + "cache-only" but that's not the behaviour I want when e.g. the variables change
these were never re-triggered in v2
Is there anywhere that documents when a query with "cache-first" is refetched? Couldn't find the rules anywhere.

calebpanza pushed a commit to christfellowshipchurch/web-app-v2 that referenced this issue Sep 20, 2021
@ghun131
Copy link

ghun131 commented Dec 4, 2021

I am having a problem related to what @sinn1 mentioned above

At first nextFetchPolicy: catch-first didn't work for me but I found that it was because the fields returned from the mutation didn't match the fields that were specified in the query.

This seems like a breaking change not mentioned anywhere? Or maybe I'm missing something. But when upgrading to v3, I have a lot of queries being refetched 2, 3, 4 times because a subsequent query fetches only a subset of the data. But I'm fetching data subsets everywhere. Example:

useGetAccount
{
  id
  first_name
  last_name
}
useGetItem
{
  id
  account {
    id
    first_name
  }
}

useGetItem triggers useGetAccount to get fetched again, with "cache-first". only way to solve it is using ""cache-first" + "cache-only" but that's not the behaviour I want when e.g. the variables change these were never re-triggered in v2 Is there anywhere that documents when a query with "cache-first" is refetched? Couldn't find the rules anywhere.

I experience the same thing. Queries same __typename with different shape tend to trigger one another without any instruction to do so.

calebpanza added a commit to christfellowshipchurch/web-app-v2 that referenced this issue Dec 7, 2021
* Update Group Single Screen (#339)

* Updates default messaging for Group Cards (#338)

* Tests written

Co-authored-by: Caleb Panza <[email protected]>

* Add new "pen" icon

* Update a handful of styles on buttons

* Linter
* Border Width now set to 2px for legibility

* Updated button style on Group Single screen

* Fixes Group Member avatar layout issues

* Removed console.log

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Edit group screen (#341)

* Update Resources and set up Group Members card

* Components for displaying a Group Member

* Stories written for all components
* Component for GroupMember
* UI element for GroupMemberStatusBadge

* Check in

* Resolves error where Modal wasn't working

* Finalize the Edit Group Member

* Add a new group member modal

Co-authored-by: Caleb Panza <[email protected]>

* Quick Updates

* Caches Group Member Search Results
* Manually update cache for Group Member _updates_
* Couple of SquareAvatar missing props

* Updated the caching logic

@see apollographql/apollo-client#6760

* New API updates

* Update icons.js

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>
calebpanza added a commit to christfellowshipchurch/web-app-v2 that referenced this issue Dec 7, 2021
* Update Group Single Screen (#339)

* Updates default messaging for Group Cards (#338)

* Tests written

Co-authored-by: Caleb Panza <[email protected]>

* Add new "pen" icon

* Update a handful of styles on buttons

* Linter
* Border Width now set to 2px for legibility

* Updated button style on Group Single screen

* Fixes Group Member avatar layout issues

* Removed console.log

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Edit group screen (#341)

* Update Resources and set up Group Members card

* Components for displaying a Group Member

* Stories written for all components
* Component for GroupMember
* UI element for GroupMemberStatusBadge

* Check in

* Resolves error where Modal wasn't working

* Finalize the Edit Group Member

* Add a new group member modal

Co-authored-by: Caleb Panza <[email protected]>

* Quick Updates

* Caches Group Member Search Results
* Manually update cache for Group Member _updates_
* Couple of SquareAvatar missing props

* Updated the caching logic

@see apollographql/apollo-client#6760

* New API updates

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>
@gourlaa
Copy link

gourlaa commented Dec 21, 2021

Hi,
We have exactly the same issue for our use case. We were using fetchPolicy: 'cache-and-network' and the useQuery was fired only if the component is mounted on the 2.x version.
Now, when we were changing the sub field with a mutation,we have an unwanted reload of the main query.
The only way we found, it's to add nextFetchPolicy: 'cache-first' locally and add a refetch when we want to bypass the cache-first.
We don't understand why when we are updating the cache with a mutation, other useQuery are fired again ..
It wasn't the case on the 2.x
Best regards,
Pierre

calebpanza added a commit to christfellowshipchurch/web-app-v2 that referenced this issue Jan 4, 2022
* Feature manage group (#365)

* Update Group Single Screen (#339)

* Updates default messaging for Group Cards (#338)

* Tests written

Co-authored-by: Caleb Panza <[email protected]>

* Add new "pen" icon

* Update a handful of styles on buttons

* Linter
* Border Width now set to 2px for legibility

* Updated button style on Group Single screen

* Fixes Group Member avatar layout issues

* Removed console.log

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Edit group screen (#341)

* Update Resources and set up Group Members card

* Components for displaying a Group Member

* Stories written for all components
* Component for GroupMember
* UI element for GroupMemberStatusBadge

* Check in

* Resolves error where Modal wasn't working

* Finalize the Edit Group Member

* Add a new group member modal

Co-authored-by: Caleb Panza <[email protected]>

* Quick Updates

* Caches Group Member Search Results
* Manually update cache for Group Member _updates_
* Couple of SquareAvatar missing props

* Updated the caching logic

@see apollographql/apollo-client#6760

* New API updates

* Update icons.js

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Update Group Single Screen (#344)

* Update Group Single Screen (#339)

* Updates default messaging for Group Cards (#338)

* Tests written

Co-authored-by: Caleb Panza <[email protected]>

* Add new "pen" icon

* Update a handful of styles on buttons

* Linter
* Border Width now set to 2px for legibility

* Updated button style on Group Single screen

* Fixes Group Member avatar layout issues

* Removed console.log

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Edit group screen (#341)

* Update Resources and set up Group Members card

* Components for displaying a Group Member

* Stories written for all components
* Component for GroupMember
* UI element for GroupMemberStatusBadge

* Check in

* Resolves error where Modal wasn't working

* Finalize the Edit Group Member

* Add a new group member modal

Co-authored-by: Caleb Panza <[email protected]>

* Quick Updates

* Caches Group Member Search Results
* Manually update cache for Group Member _updates_
* Couple of SquareAvatar missing props

* Updated the caching logic

@see apollographql/apollo-client#6760

* New API updates

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>

* Email group button (#400)

* first commit

* Button Spacing

* added route

Co-authored-by: asleepingpanda <[email protected]>

* Adds layout and route for Emailing Group (#399)

* Adds layout and route for Emailing Group

* Updated Label

* Group Member Filters (#401)

* Group Member Filters

Active, Pending, Inactive

* Mobile styling

* Feature - Rich Text Editor (#402)

* Quill React package and storybook

* Adds the Rich Text Editor to the Email Composer

* Font Size updates

* Feature email composer confirmation (#404)

* Email Confirmation dialogue

* Quick path routing clean up

* Select email recepients (#405)

* first commit

* Group Member Selection local state

* Update GroupEmailComposer.js

* Adds Email Recipients Modal

* Adds Email Recipient Modal

* commit for review

* Update UI to be inline

* Deprecated modal

* Remove console.log's

Co-authored-by: Lis Alfonso <[email protected]>

* Email Recipient/Select All update

* Email a specific Group Member

* Email Confirmation Screen

* [CFDP-1896] Connected EmailComposer to API

* patch - default campus value

* Fixed styling on Group Member Detail modal

* Fixed Styling for Sender Email

Co-authored-by: Caleb Panza <[email protected]>
Co-authored-by: dzwood <[email protected]>
Co-authored-by: Lis Alfonso <[email protected]>
bchrobot added a commit to politics-rewired/Spoke that referenced this issue Jan 7, 2022
Writing interaction steps to the cache caused the GET_EDIT_CAMPAIGN_DATA query to be re-fetched.
The results of re-fetch then overwrote the local changes for pending interaction step changes. This
re-fetch happened because in apollo-client@3 cache-and-network fetch policies re-run whenever there
is a cache update that affects the query.

For more information, see:
apollographql/apollo-client#6760 (comment)
bchrobot added a commit to politics-rewired/Spoke that referenced this issue Feb 2, 2022
Writing interaction steps to the cache caused the GET_EDIT_CAMPAIGN_DATA query to be re-fetched.
The results of re-fetch then overwrote the local changes for pending interaction step changes. This
re-fetch happened because in apollo-client@3 cache-and-network fetch policies re-run whenever there
is a cache update that affects the query.

For more information, see:
apollographql/apollo-client#6760 (comment)

Adapts to two apollo client changes:
1. readFragment returns null for insufficient data instead of throwing MissingFieldError exceptions
2. writeFragment now only writes to fields present in the fragment itself

Reviewed-by: @ben-pr-p
@dancrew32
Copy link

dancrew32 commented Feb 2, 2022

UPDATE 9/27/22: This doesn't work anymore. This got so much more complicated than it needs to be:
https://www.apollographql.com/docs/react/data/queries/#nextfetchpolicy
You should just specify fetchPolicy and nextFetchPolicy in each useQuery instead of writing some complicated watchQuery function that's going to break again in the future.

--

Huge shoutout to @stangerjm for their proposed fix. Documenting our fix here:

  1. Upgraded from ApolloClient V2 to V3
  2. Noticed a problem with react-beautiful-dnd onDragEnd calling a mutation that had optimisticResponse, which previously would not show a temporary revert of sort order (correct behavior), suddenly started showing a temporary revert of sort order (incorrect behavior) until the mutation successfully updated the cache.
  3. Our useQuery hook had an explicit fetchPolicy: 'network-only', because we wanted to avoid showing a stale list.
  4. Discovered that setting fetchPolicy in the useQuery hook, would implicitly set nextFetchPolicy with the same policy, but only if there were no defaultOptions set in the ApolloClient instance.
  5. Tested removing fetchPolicy: 'network-only' from useQuery and observed correct drag & drop behavior, so this proves that the override was the problem.
  6. Set fetchPolicy: 'network-only' and nextFetchPolicy: 'cache-first' on the useQuery and observed that the problem was fixed. However, having to set nextFetchPolicy on every useQuery is impractical.
  7. Thanks to @stangerjm, we tested the following default and now everything is working correctly. We decided to omit fetchPolicy as a default from defaultOptions.watchQuery so that it uses the framework default of cache-first. Only specifying nextFetchPolicy: 'cache-first' fixes the problem for us.
export const client = new ApolloClient({
  link,
  cache,
  defaultOptions: {
    watchQuery: {
      nextFetchPolicy: 'cache-first',
    },
  },
});

@s123121
Copy link

s123121 commented May 21, 2022

I experience the same thing. Queries same __typename with different shape tend to trigger one another without any instruction to do so.

As other people have pointed out, using nextFetchPolicy: cache-first does not help when the data change shape. So there are more nuances in this problem and this should be fixed inside apollo-client

@drewlustro
Copy link

Would love to see an update from official maintainers, such as @benjamn, on this matter. The documentation remains incomplete, migration guide makes no mention of fetch policy behavior, and the community cannot help without word about what configuration is necessary to achieve apollo-client@v2 behavior.

@twittwer's note on optimistic response no longer working under nextFetchPolicy: 'cache-first' is particularly of concern.

@bezreyhan
Copy link

@benjamn just to give an update, upgrading to v3.6.0 did not resolve the issue for me. What's even more problematic for me is that my onComplete callback is getting called multiple times.

I currently have to use a ref to track if the onComplete callback was run. It's not so elegant, but it works. The query is still running multiple times though.

@efstathiosntonas
Copy link
Contributor

efstathiosntonas commented Sep 27, 2022

in my case @sinn1 comment helped me a lot debugging the issue of refetch after the mutation and cache update. The issue was that the mutation query results keys weren't the same ones as from the query causing a network fetch in order to properly update the cache. Thanks @sinn1.

Why would we have to get back the exact same result keys from the mutation and increase the size of the response?

@sreubenstone
Copy link

sreubenstone commented Nov 14, 2022

I’ve been wrestling with this issue for a few weeks..to no avail. I updated Apollo Client to v3.7.1 hoping this would help, but it has not. I have also read through this thread extensively and another similar thread.

What I'm trying to accomplish...

Load news feed by calling getFeed query.

getFeed loads…

posts (__typename: ‘Comment’)
upvotes
  id
  user_id
  user
    id
    user_avatar
replies
  edges (__typename: ‘Comment’)
    upvotes
      id
      user_id
      user
        id
        user_avatar

  • When I use writeFragment (after I fire a mutation off to save or delete an upvote) on a reply's upvote, I get no network refetch of getFeed (expected behavior).
  • When I use writeFragment on a post’s upvotes, I get the network refetch (unexpected behavior).

Does anyone know any workarounds to this issue? I have tried many combinations of fetchPolicy and nextFetchPolicy. The only other thing I can think of from reading through the docs is potentially a requirement for a custom merge function…but that seems like overkill.

@leymytel
Copy link

@sreubenstone

I was experiencing the same issue with arrays that had different IDs after an update.

I fixed this issue by adding the same fields in the mutation as the query.

According to the documentation here: https://www.apollographql.com/docs/kotlin/caching/troubleshooting/

A single missing field in the cache for a query will trigger a network fetch. Sometimes it might be useful to query an extra field early on so that it can be reused by other later queries.

@sreubenstone
Copy link

@leymytel thanks for the reply. In my case, the saveUpvote mutation returns type Upvote (I am adding a new Upvote to the upvote array on a specific Comment in my news feed). The fields I'm returning off the saveUpvote mutation are identical to the field structure for upvotes that come in on Comments when I query getFeed. Do I need to return type Comment off the saveUpvote mutation? I did try this as well (returning the entire Comment, not just the Upvote, on the saveUpvote mutation, and it did not work).

I'm getting lost on the "missing a single field". I have no fields missing that I can see. The upvote returned matches the upvote queried for in getFeed.

mutation saveUpvote($comment_id: Int) {
    saveUpvote(comment_id: $comment_id) {
      id
      user_id
      user {
        id
        user_avatar
      }
    }
  }

@alessbell
Copy link
Member

Hi everyone 👋 I've gone ahead and called this out in the migration docs for posterity via #10714.

I'm going to close this issue out as I see no other actionable items, but please feel free to open a new issue should the need arise. Thanks!

@alessbell alessbell closed this as not planned Won't fix, can't repro, duplicate, stale Apr 3, 2023
@github-actions
Copy link
Contributor

github-actions bot commented May 4, 2023

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
For general questions, we recommend using StackOverflow or our discord server.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 4, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.