react-apollo
react-apollo copied to clipboard
useQuery() refetch throws on error instead of flowing through hook
Consider something like:
const {loading, error, data, refetch} = useQuery(someQuery)
if (error) {
return (
<span>
Could not fetch that.
<button onClick={() => refetch()}>Try again</button>
</span>
)
}
return ...
If the query fails, the error message will be shown. But the provided refetch()
function does not integrate with the hook, and will in fact throw an exception on further failure. I expected errors to flow back into the hook, but this is not the case. Further, refetch()
does not update loading
or networkStatus
.
This means any component using refetch
will have to handle errors (and retrying) in two different and incompatible ways. Perhaps something like:
const [refetchError, setRefetchError] = useState<any>(undefined)
const [retrying, setRetrying] = useState<boolean>(false)
const {loading, error, data, refetch} = useQuery(someQuery)
if (error || refetchError) {
return (
<span>
Could not fetch that.
<button onClick={() => {
setRetrying(true)
try {
refetch()
} catch (err) {
setRefetchError(err)
} finally {
setRetrying(true)
}
}}>Try again</button>
</span>
)
}
return ...
This is ugly.
It appears that setting notifyOnNetworkStatusChange: true
fixes this — error and load status flows back to the hook. Is this a bug? This is, at least, not obvious from the documentation.
It looks like it throws when using network-only
fetch policy, but flows through the hook when using cache-and-network
. I don't see this behavior documented anywhere so it's hard to tell if it's on purpose
I'm also running into this issue. In our case, above mentioned proposals did not result in the data flowing through the hook.
We call refetch with a new set of variables as outlined in the docs, but the result of this is not reflected in the data
or loading
fields from the useQuery hook.
However, calling refetch is returning a promise which resolves into a query result ({data, loading, error etc..}
). this would be an unwieldy way of making a hook implementation work since it would requires us to save this new result in a new state field, duplicating our data fetching logic.. 🤷♂
Would it be nice to outline this use-case (refetching data icm useQuery) and the best practices in the docs?
+1 can confirm that refetch()
throwing does not update the error
returned by useQuery
, does not seem to be affected by fetchPolicy
or notifyNetworkStatusChange
for me.
This makes doing retry on errors very difficult.
The example by the original poster would break if the props that useQuery
used for its variables changed triggering a new query as loading
or error
would be out of step with retrying
and refetchError
. You would need to add an additional useEffect
that cleared those fields when any query relevant props change.
"@apollo/react-hooks": "3.1.3"
I wrote a workaround for this:
// useQuery has a bug where an error in refetch does not change its error property
export default function useRetryableQuery(query, options = {}) {
const [refetchError, setRefetchError] = useState(undefined);
const result = useQuery(query, options);
const variables = options.variables || {};
useEffect(() => {
setRefetchError(undefined);
}, Object.values(variables));
return {
...result,
networkStatus: refetchError ? 8 : result.networkStatus,
error: refetchError || result.error,
refetch: () => {
setRefetchError(undefined);
return result.refetch().catch(err => {
setRefetchError(err);
return Promise.reject(err);
});
},
};
}
Unfortunately there is another bug in useQuery
that after the first error it doesn't seem to trigger any more be it from a change in variables or a refetch()
call. I am unable to work around that without reimplementing useQuery
.
https://github.com/apollographql/react-apollo/issues/3800
@hwillson can you take a look at this? It's a pretty important issue. Not being able to handle errors correctly in Apollo is frustrating.
I'm seeing this error with useMutation and the fact I can't catch an error in a mutation is super hard to provide a solid UX for the users of my application.