Reputation: 1677
I'd like to catch the error in component level and prevent propagation while using the useQuery
in @apollo/react-hook.
Here is my example code
const invitationDocument = gql`
query DecodeInvitation($token: String!) {
DecodeInvitation(token: $token) {
name
email
}
}
`
const InvitationPage = (props) => {
const { data, error, loading } = useQuery(invitationDocument, {variables: { token: "XXXX" }});
if(error)
{
return <InvitationErrorPage error={error.message}/>
}
return loading? <LoadingPage> : <InvitationAcceptPage />
}
It works fine but at the same time, the error is being propagated to its parents level so I get another error notification message which comes from the error handler at the global level. At the application level, I use the apollo-link-error to manage the Graphql errors.
import { onError } from 'apollo-link-error';
const errorLink = onError (({ graphqlErrors, networkError }) => {
if(graphqlErrors)
notification.error(graphqlErrors[0].message);
});
const client = ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([
errorLink,
new HttpLink({ uri: `http://localhost:8080/graphql`})
])
})
For now, I am finding a solution to stop propagation to top-level so that I can show only InvitationErrorPage
and stop displaying error notification at the global level.
Upvotes: 1
Views: 3098
Reputation: 511
I hope this could help anybody. In my case I did the following:
On the component I'm performing the query:
const { data, loading } = useQuery({
variables: { ...whatever variable },
context: { errorHandledLocally: true }, // We don't want to handle this error globally
onError: (error) => {
// Do whatever you want with the error
// In my case I want to show a toast with an specific error
}
})
Then, in the apollo client, I have the following link:
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message }) => Sentry.captureException(message))
if (operation.getContext().errorHandledLocally) return // Check this into the queries performed throughout the app
toaster.negative(default_error)
}
if (networkError) {
toaster.negative(network_error)
Sentry.captureException(networkError)
}
})
Basically with the flag errorHandledLocally I'm making sure that I'm not rendering two toasts in this case.
Upvotes: 0
Reputation: 73
I was also trying to prevent errors from being logged in an Error Link by handling them on a useQuery
hook and a further delve into the ApolloLink documentation helped clear up what is happening. The key misunderstanding is that the Error Link is not a parent- or application-level handler, it is request middleware. It's helpful to think about how the data is coming back from the server:
Thus, when you see an error notification from the Error Link it is not something that "propagated up" from the useQuery
hook: it occurred in the request path before the useQuery
result was available on the client.
Thus, the onError
callback for the Error Link will always be called before any error handling code in the useQuery
hook.
Probably your best bet is to use a combination of the operation
and graphQLErrors[x].extensions
to figure out what errors you should pass through the Error Link middleware like so:
const errorLink = onError(({operation, response, graphQLErrors}) => {
if (!graphQLErrors) {
return;
}
if (operation.operationName === "DecodeInvitation") {
for (const err of graphQLErrors) {
if (err.extensions?.code === 'UNAUTHENTICATED') {
// Return without "notifying"
return;
}
}
}
// Notify otherwise
notification.error(graphqlErrors[0].message);
})
Upvotes: 4