apollo-client: fetchMore and fetchPolicy: cache-and-network does not work together
Intended outcome:
In case of fetchMore additional request should not be executed or should use last variables (passed to fetchMore)
Actual outcome: The request executes with wrong variables
How to reproduce the issue:
function MyComponent() {
const q = useQuery(query, { variables: { offset: 0 }, fetchPolicy: 'cache-and-network' });
...
<button id="button" onClick={() => q.fetchMore({ variables: { offset: 10 } })}>Click me</button>
}
That’s what is happening:
<MyComponent />is rendered => request to the server is executed with{ offset: 0 }- Button
#buttonclicked => request to the server is executed with{ offset: 10 }=> request to the server is executed with{ offset: 0 }
In other words the request, which fetches fresh data from the server uses variables passed to the hook, but to to fetchMore
Versions
System:
OS: Windows 10 10.0.19041
Binaries:
Node: 12.18.0
Yarn: 1.22.4
npm: 6.14.4
Browsers:
Chrome: 84.0.4147.125
npmPackages:
@apollo/client: ~3.1.2 => 3.1.3
apollo: ~2.30.2 => 2.30.2
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 33
- Comments: 31 (5 by maintainers)
Commits related to this issue
- Avoid full reobservation after `fetchMore`. https://github.com/apollographql/apollo-client/issues/6916#issuecomment-901466918 — committed to benjamn/apollo-client by benjamn 3 years ago
- Avoid full reobservation after `fetchMore`. https://github.com/apollographql/apollo-client/issues/6916#issuecomment-901466918 — committed to apollographql/apollo-client by benjamn 3 years ago
- Avoid full reobservation after `fetchMore`. https://github.com/apollographql/apollo-client/issues/6916#issuecomment-901466918 — committed to apollographql/apollo-client by benjamn 3 years ago
- Avoid full reobservation after `fetchMore`. https://github.com/apollographql/apollo-client/issues/6916#issuecomment-901466918 — committed to apollographql/apollo-client by benjamn 3 years ago
- Avoid full reobservation after `fetchMore`. https://github.com/apollographql/apollo-client/issues/6916#issuecomment-901466918 — committed to apollographql/apollo-client by benjamn 3 years ago
See also #6327 and #6833, especially this comment.
Looks like the proper policy combination in 2021 for paging+cache is
After thinking more about this, I agree with @Akryum the extra network fetch after
fetchMoreis not a reasonable consequence of usingnetwork-onlyorcache-and-networkfetch policies.Yes, you may be able to work around the problem with
nextFetchPolicy: "cache-first", but I no longer think usingnextFetchPolicyshould be necessary in this case. As long as the cache read triggered byfetchMoreis complete (in thediff.completesense), I think we should deliver that cache result immediately, and skip the network. In other words, reacting to afetchMorerequest is not one of the situations where we want/need to reapply the original query’sfetchPolicyliterally, so I think it should be safe to ignore thefetchPolicyhere, and just deliver the result.I’m still investigating the consequences of these changes, but I think/hope we can remove the
this.reobserve()from thefinallyafterfetchMore, and change theQueryInfocode as follows:The
observemethod ofObservableQuerydelivers the current result only, whereasreobservereapplies thefetchPolicy, potentially delivering multiple results.I hope to have more answers soon. Thanks to @akryum for sharing your findings!
Some good news, at long last: using @Akryum’s reproduction and
@apollo/client@3.6.0-beta.10, I see no extra network requests after thefetchMorecalls (thanks to #9504). These changes have just landed (today) on therelease-3.6branch, so we are actively looking for help testing them.While this change seems like the right behavior for most use cases, there’s a chance it will be disruptive for code that was previously relying on
fetchMoretriggering extra network requests (hard to imagine, but certainly possible). If you’re reading this, please help us test/validate these changes by updating your projects withnpm i @apollo/client@betawhen you have the chance! Your help will get v3.6 released sooner (in April).Reproduction: https://codesandbox.io/s/apolloclient-beta-fetchmore-bug-forked-8mplu?file=/src/App.js
Click multiple times on the load more button:
Indeed apollo client does a “phantom” request after the fetch more one:
I had a similar issue to @zakton5. I originally though it was a
fetchPolicyissue, but what was actually causing the behavior was a custom merge function for a field that originally looked likeThat field could be null for some of my data while I was doing a
fetchMore, soexisting=undefinedandincoming=null. My merge function would returnundefined, which must’ve triggered some silent error causing apollo to retry the original query, with the network status going from 3 -> 1.Changing my merge to be:
caused the issue to stop happening.
what is the solution at the moment ? still have this issue with
@apollo/client": "^3.5.8, I have disabled local cache.I can also confirm this behavior, but I can’t use the workaround. I really need
cache-and-networkto trigger as soon as my variables change (every time the variables change). It seems thefetchPolicy + nextFetchPolicydoesn’t really do it for me 😕 Is this planned at all? seems like it’s been more than a yearI’ve just spent ~3.5h debugging my code, then Apollo, investigating what appears to be the same issue as described, in a lazy loading list view.
Fetch more is correctly fetching and merging the next page of data, but a second request without the pagination token was then being sent and replacing the data in merge, causing the page content to reset and reset the scrollbar to the end, tiggering a refetch of the second page again, repeating infinitely
@khitrenovich suggestion to set
appears to suppress the second call and things now seem to work as expected.
What’s frustrating is this query is only used in one place, so I’m not sure what exactly is subscribed to be notified which could be triggering this behavior.
If I remove the
reobservefromfetchMore, there is another one called as a side-effect of thiswriteQuery: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/ObservableQuery.ts#L372This
writeQuerycall will lead tosetDiffbeing called as well, but more importantlynotify: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/QueryInfo.ts#L205This then calls a
reobservetoo, causing a new request with the initial variables/options to be sent: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/QueryInfo.ts#L233Then the result from
fetchMoreis reverted by the newfetch.Having the same issue in Apollo Client 3.3.13 Using
fetchPolicy: 'network-only'andnotifyOnNetworkStatusChange: true, callingfetchMorewill trigger an unwanted additionnal network fetch which will mess up with the type policymerge.I can’t use
nexFetchPolicysince I’m usingnetwork-only. Please send help 🏳️Digging into the source code…
The unwanted fetch seems to be caused by this line: https://github.com/apollographql/apollo-client/blob/336337dbedc108f13035f85eacf5dde1b47ddfd8/src/core/ObservableQuery.ts#L333 Which in turn calls this with the initial variables/options: https://github.com/apollographql/apollo-client/blob/336337dbedc108f13035f85eacf5dde1b47ddfd8/src/core/Reobserver.ts#L52
On 3.4 the lines are all in
ObservableQuery.tsbut the logic is the same: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/ObservableQuery.ts#L383 And the fetch: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/ObservableQuery.ts#L658Not sure what a concast is though.
@alex-golubtsov I believe you’re describing a related issue to the one mentioned here -> https://github.com/apollographql/apollo-client/issues/6907
Addition from myself: The same issue goes for the
network-onlypolicy. The only one that is working properly iscache-first.useQuery/useLazyQuery-> same effect.For any poor soul who couldn’t figure this out like me. My problem was unique in that I had set custom
keyFieldsfor the specific type I was querying for.The problem for me specifically was that
targetcould sometimes be null. I think Apollo got upset when this happened because it was trying to accesstarget.entityIdand couldn’t. So maybe there was an internal error (no logs about it) and it just retried the query with the default variables/arguments?My fix was to just remove
entityIdentirely.targetwas sufficient.Any update? At this moment I can’t use fetch more or create an infinite scroll 😭.
There are some workaround to make this fetchMore?
So what’s the verdict here? Is it reasonable to expect/say this is a valid solution and no harm comes from it - hence it’d make it onto the next release ❤️? (Wishful thinking - I know, but hey, what’s there if there’s no hope?)
There is probably an architectural issue if a
notify()causes a full newfetch?I tried again on the codesandbox with 3.4.0-rc.23. The issue is kinda still here on the network side: the second phantom/unnecessary request with
first: 1is still made, but it doesn’t call themerge(so it doesn’t break pagination). So the bug halfway-fixed! 🔧Any update on people trying to fix this???