Intended outcome:
Using an onError function with useQuery allows a component to recover from network errors:
const {data, loading} = useQuery(QUERY, {
onError: (e) => { // do something with e },
});
Actual outcome:
When a network error occurs and an inline onError function is defined (as show above), an infinite loop occurs, causing useQuery to make repeated requests indefinitely.
Note: This issue does not occur when a "stable reference" to a function is supplied as a value for onError.
// This version works without issue.
const onError = () => {};
function Component() {
const {data, loading} = useQuery(QUERY, {
onError,
});
// ...
}
How to reproduce the issue:
import React from 'react';
import ReactDOM from 'react-dom';
import {ApolloClient, HttpLink, InMemoryCache, ApolloProvider, gql, useQuery} from '@apollo/client';
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
uri: 'https://forceNetworkError',
}),
});
const QUERY = gql`
query {
aField
}
`;
const Root = () => {
return (
<ApolloProvider client={client}>
<Component />
</ApolloProvider>
);
};
// Replacing the inline version of onError (in useQuery below) with this fixes the issue.
// const onErrorStableReference = () => {};
let renderCount = 0;
function Component() {
renderCount++;
if (renderCount === 100) {
throw new Error("That's enough :)");
}
const {loading, error} = useQuery(QUERY, {
onError: () => {},
// onErrorStableReference,
});
if (error) return <p>Error loading query :(</p>;
else if (loading) return <p>Loading query...</p>;
else return <p>We'll never get here</p>;
}
ReactDOM.render(<Root />, document.getElementById('root'));
Versions
System:
OS: macOS Mojave 10.14.6
Binaries:
Node: 14.1.0 - /usr/local/bin/node
npm: 6.14.4 - /usr/local/bin/npm
Browsers:
Chrome: 83.0.4103.97
npmPackages:
@apollo/client: 3.0.0-rc.2 => 3.0.0-rc.2
react: 16.13.1
Using useQuery with "network-only" fetch policy seems to also trigger a loop.
The combination of an inline error handler and cache-and-network fetch policy seems to cause this too.
If you use useCallback like this code, the problem will be resolved!
Btw I'll look into the code and find the reason ASAP.
I think the same issue happens with onCompleted too.
I just encountered this and put together an example as well before I discovered this issue. As it turns out, if you return any false-y value from your onError handler then the infinite loop occurs, but any truth-y thing will break the infinite loop.
Can confirm that this happens with onCompleted as well, and the above suggestion to use a stable reference from useCallback fixes the issues. We're on rc.8.
I just encountered this and put together an example as well before I discovered this issue. As it turns out, if you return any
false-yvalue from youronErrorhandler then the infinite loop occurs, but anytruth-ything will break the infinite loop.
This no longer works in latest stuff, but the useCallback solution does. 馃憤
This is still happening even with the official v3 release. The component infinitely rerenders and fetches if I pass an onCompleted fn.
I believe the fix will land in the next version as it was merged only 2 days ago
https://github.com/apollographql/apollo-client/pull/6588
Update to @apollo/[email protected] version while v3.1.0 is not published.
I can confirm that with @apollo/[email protected] this is fixed. Thank You.
Most helpful comment
I just encountered this and put together an example as well before I discovered this issue. As it turns out, if you return any
false-yvalue from youronErrorhandler then the infinite loop occurs, but anytruth-ything will break the infinite loop.