-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Not memoize onCompleted and onError never resolves the query #6301
Comments
@hwillson I've encountered this issue as well. The result is an infinite number of API calls which means the same as our own frontend doing a DDoS attack on our API. We've reverted back to version I've narrowed the issue with our setup to be exactly the non-memoized This is what our component could look like: function MyComponent() {
const { data } = useQuery(MY_QUERY, {
ssr: false,
onError: () => { /* ... */ }
});
return <p>Hello</p>;
} If I comment the client.defaultOptions = {
watchQuery: {
fetchPolicy: "no-cache",
errorPolicy: "ignore",
},
query: {
fetchPolicy: "no-cache",
errorPolicy: "none",
},
mutate: {
errorPolicy: "none",
},
}; Which, as far as I know, should essentially disable caching altogether. Now, if I comment out the So, for me, the infinite query fetching seems to result from two different configurations:
|
I'm having the same infinite loop but using cache-and-network. It seems this can be reproduced in the original codesandbox by changing the fetch policy to cache-and-network and reloading the page. |
Currently trying to migrate to v3 but got hit with similar issue (using |
It's because of this equality check on the options: https://github.com/apollographql/apollo-client/blob/master/src/react/data/QueryData.ts#L243:L243. It makes sense, I mean if you pass new options to the query then you probably want it to re-run like if you switched variables it should query with. The issue is with inline function since they'll be newly created each render. Assuming this is intended behavior, the solve is to memoize your functions. |
@danReynolds You are right, the issue points out that the solution is to memoize the If the change of behaviour is intended, that's cool but just need to be documented otherwise I can only see this behaviour change as an unintended bug. |
Thanks for the suggestion to memoize with Forking the above-linked CodeSandbox, I can still reproduce this with the latest As discussed in apollographql/react-apollo#4008, is it deliberate that Apollo Client will now effectively require those callbacks to be memoized? It's not obvious to me that they should be included in the equality check as mentioned above, although I'm not familiar enough with the internals of the client code to comment more decisively. From a "developer experience" point of view though, it's certainly non-intuitive to have to memoize those functions, particularly when this was not the case prior to |
This resolves the issue described at apollographql/apollo-client#6301 Perform Yarn upgrade. Bump minor version.
Looks more like a bug and is not documented... is anyone who actual written the code and understand the right behavior in this chat? The repo owners? |
Any update to this? It's really not documented nor an intuitive solution to have to memoize onCompleted/onError... |
`QueryData` stores last used options to help decide when it should re-run. If new options (when compared against the previously stored last options) are found, `QueryData` will make sure the new options are passed into Apollo Client for processing. When `onCompleted` and/or `onError` options are set however, `QueryData` thinks the options received on each render are new as these callback functions don't have a stable identity. This can then lead to infinite re-renders. This commit adjusts the `QueryData` option equality check to ignore option callbacks. During normal use of `useQuery` it should be okay to ignore callbacks like this, as they don't normally change between renders. Fixes #6301
`QueryData` stores last used options to help decide when it should re-run. If new options (when compared against the previously stored last options) are found, `QueryData` will make sure the new options are passed into Apollo Client for processing. When `onCompleted` and/or `onError` options are set however, `QueryData` thinks the options received on each render are new as these callback functions don't have a stable identity. This can then lead to infinite re-renders. This commit adjusts the `QueryData` option equality check to ignore option callbacks. During normal use of `useQuery` it should be okay to ignore callbacks like this, as they don't normally change between renders. Fixes #6301
`QueryData` stores last used options to help decide when it should re-run. If new options (when compared against the previously stored last options) are found, `QueryData` will make sure the new options are passed into Apollo Client for processing. When `onCompleted` and/or `onError` options are set however, `QueryData` thinks the options received on each render are new as these callback functions don't have a stable identity. This can then lead to infinite re-renders. This commit adjusts the `QueryData` option equality check to ignore option callbacks. During normal use of `useQuery` it should be okay to ignore callbacks like this, as they don't normally change between renders. Fixes #6301
Is this possibly related to #6416 ? |
`QueryData` stores last used options to help decide when it should re-run. If new options (when compared against the previously stored last options) are found, `QueryData` will make sure the new options are passed into Apollo Client for processing. When `onCompleted` and/or `onError` options are set however, `QueryData` thinks the options received on each render are new as these callback functions don't have a stable identity. This can then lead to infinite re-renders. This commit adjusts the `QueryData` option equality check to ignore option callbacks. During normal use of `useQuery` it should be okay to ignore callbacks like this, as they don't normally change between renders. Fixes #6301
The problem is outlined and fixed in #6588. That PR hasn't been merged yet however, as we aren't entirely happy with the solution. We're considering migrating parts of |
Just to add, we're now in a code freeze as we prep for the 3.0 launch (tomorrow 🎉), which means the fix for this won't make it in for 3.0. We'll have it out in a 3.0.1 shortly after the 3.0 launch. Thanks for your patience all! |
Will this fix leave this with the same behaviour of version 2.6? In that version, these callbacks are updated when the component rerenders. Will they be memoized and never be updated in your fix? Edit: I just found the commit and according to the code, seens like it will just work like version 2.6. Can you confirm this? |
I'm still experiencing this behaviour, even with onCompleted and onError functions memoized. I'm attempting to migrate from react-apollo and am not having any luck. Is a fix coming for this? Edit: I have also started removing the onCompleted and onError functions altogether, and just checking the loading, error and data properties returned from useQuery and useMutation. But still have the same issue. Infinite rendering |
Did you try 3.2.4? |
@eduleite Just downgraded now and I still get the infinite render. |
@eduleite I have tried on 3.2.2, 3.2.4, and 3.2.5, I have pared component down to
And even this minimal example loops forever. Am trying to find any version >= 3.0.0 that doesn't have this bug. Just upgraded from 2.6.4 and it was a beast, would love not to have to revert those commits |
I was facing the same problem until I realised my useCallback is missing the dependency list. This workaround works at the latest stable version |
@lauyilouis I still have no luck. I have my onCompleted as you do |
@lauyilouis That being said, I'm working on a feature where all api calls will be made through a webworker. So far the prototype works, now going to try it on a branch and see if we can replace all our api calls with it and remove Apollo. This indefinite render has left us stuck on our current version and that's not going to work for us. |
@TerrySlack i'm facing the same issue, tried to downgrade, useMemo, etc an still having infinite loop. What was your solution? did you had to create that webworker feature that you mentioned? |
@pcunha29 I’m sorry to hear that. Do you have a runnable example? 👉👈 I keep trying to context-switch and recreate all these issues on my own and then I end up finding out it’s already been fixed. I’m happy to reopen the issue if can create a minimal example! |
@pcunha29 I put the project on hold, but I will look into it tomorrow and see if I can get it finished for end of next week. Sorry for the delay. I feel your pain and have completely moved away from Apollo. |
@brainkim and @TerrySlack thanks for your response! ✋ in the meantime, i was able to solve this with |
@pcunha29 That's good news. I had no luck with useCallback at all. Really strange. I tried updating 3 different projects. The updates worked in 1 project and the other two had the infinite rendering. Still, I plan on finishing my web worker based one and hope to put it out there as an npm package. |
@TerrySlack i'll leave this here, in case that helps you. That's how I have it working now
@apollo/[email protected] |
@pcunha29 I tried that. No luck. All good though. We have high hopes for the worker. I put it on hold, got distracted and am glad to have gotten a ping from you today. Time to dust it off and finish this. |
Reopening just so I can keep track of it 👀 Still looking for a reproduction against the latest version. |
Never mind I’m leaving it closed but I have my eye on it. Sorry! |
I'm using version 3.3.20 and still having the same issue, added useCallback to the onCompleted and onError method and still the onCompleted gets fired infinitely |
For me, the only thing that solved this was using |
Hi @hwillson, |
@AliRehman7141 as mentioned in #6301 (comment), can you provide a reproduction? If so we'll re-open. Thanks! |
Reproduction is same as the initial comment of this thread |
I am seeing this as well on 3.5.10 |
Just encountered this problem after we upgraded to React 18 and React Native 0.69.7. We have version Example: // Problematic code
const {data} = useSomeQuery({onCompleted: () => console.log('Completed')}); // Memoized function (the fix)
const onCompleted = React.useCallback(() => console.log('Completed'), []);
const {data} = useSomeQuery({onCompleted}); |
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. |
Intended outcome:
The following query should log either
Completed
orError
to the console.Actual outcome:
The query is never resolved and the component re-renders infinitely.
Is it now recommended to make sure to memoize the query/mutation callbacks (
onCompleted
,onError
,update
, etc) when used withfetchPolicy: 'network-only'
? The version,@apollo/[email protected]
resolved the callbacks fine without having to memoize them.How to reproduce the issue:
I've created a reproduction on the Code Sandbox: https://codesandbox.io/s/sleepy-mendeleev-1q9jf?file=/src/App.js
Open up the console and you will see
loading
being logged in the console infinitely. If you however comment the non-memoizedonCompleted
andonError
and uncomment the memoized version and refresh the browser (as below), it will successfully resolve the query and show the data.Versions
Reproducible from @apollo/client:
3.0.0-beta.46
The text was updated successfully, but these errors were encountered: