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
#button
clicked => 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
fetchMore
is not a reasonable consequence of usingnetwork-only
orcache-and-network
fetch policies.Yes, you may be able to work around the problem with
nextFetchPolicy: "cache-first"
, but I no longer think usingnextFetchPolicy
should be necessary in this case. As long as the cache read triggered byfetchMore
is complete (in thediff.complete
sense), I think we should deliver that cache result immediately, and skip the network. In other words, reacting to afetchMore
request is not one of the situations where we want/need to reapply the original query’sfetchPolicy
literally, so I think it should be safe to ignore thefetchPolicy
here, 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 thefinally
afterfetchMore
, and change theQueryInfo
code as follows:The
observe
method ofObservableQuery
delivers the current result only, whereasreobserve
reapplies 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 thefetchMore
calls (thanks to #9504). These changes have just landed (today) on therelease-3.6
branch, 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
fetchMore
triggering 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@beta
when 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
fetchPolicy
issue, 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=undefined
andincoming=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-network
to trigger as soon as my variables change (every time the variables change). It seems thefetchPolicy + nextFetchPolicy
doesn’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
reobserve
fromfetchMore
, there is another one called as a side-effect of thiswriteQuery
: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/ObservableQuery.ts#L372This
writeQuery
call will lead tosetDiff
being called as well, but more importantlynotify
: https://github.com/apollographql/apollo-client/blob/7856c187ebf89df80c446834ab238e5475e6114d/src/core/QueryInfo.ts#L205This then calls a
reobserve
too, 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
fetchMore
is reverted by the newfetch
.Having the same issue in Apollo Client 3.3.13 Using
fetchPolicy: 'network-only'
andnotifyOnNetworkStatusChange: true
, callingfetchMore
will trigger an unwanted additionnal network fetch which will mess up with the type policymerge
.I can’t use
nexFetchPolicy
since 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.ts
but 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-only
policy. 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
keyFields
for the specific type I was querying for.The problem for me specifically was that
target
could sometimes be null. I think Apollo got upset when this happened because it was trying to accesstarget.entityId
and 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
entityId
entirely.target
was 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: 1
is 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???