React-apollo: onError callback fired twice

Created on 1 Nov 2018  路  12Comments  路  Source: apollographql/react-apollo

import React, { Component } from 'react';
import Layout from './Layout';
import { withNamespaces } from 'react-i18next';
import Spinner from '../Spinner';
import gql from 'graphql-tag';
import { Query } from 'react-apollo';
import { withError } from '../../ErrorContext';

const PROFILE_QUERY = gql`
  query CurrentUser {
    me {
      username
    }
  }
`;

class ChangePasswordForm extends Component {
  render() {
    return <div />;
  }
}

export default withError(
  withNamespaces()(props => (
    <Layout title={props.t('settings')}>
      {console.log('rendering')}
      <Query
        query={PROFILE_QUERY}
        fetchPolicy="network-only"
        onError={console.log.bind(console)}
      >
        {({ client, loading, data, error }) => {
          if (error) return null;
          if (loading) return <Spinner />;
          return (
            <div>
              <div>Username: oops</div>
              <ChangePasswordForm data={data} />
            </div>
          );
        }}
      </Query>
    </Layout>
  ))
);

For some reason, console.log is invoked twice with the same error. Is this a bug?

confirmed

Most helpful comment

I believe this code from apollo-client is partially to blame, since ObservableQuery passes onError to the error property of the observer and to the catch handler of the promise, so if the first fetchQuery call fails on a network error, both places will catch the error.

We _could_ just put a simple if (this.lastError !== error) around this line to stop ObservableQuery from reporting the same error twice, but this won't work. This is because react-apollo resets the ObservableQuery.lastError property after it receives an error, which means the next time an error comes in, lastError will always be undefined.

I would very much like to fix this since it's an unnecessary annoyance. 馃檪

All 12 comments

Seems to only fire twice for me if i have fetchPolicy="cache-and-network"

I found a basic solution to this that should work in all scenarios.

It turns out that the object is identical in all calls, you could set up something to match them exactly, or you could just attach a custom attribute to the error...

Admittedly this may only work with window.addEventListener('error', function...), as you are given the genuine error object as an argument, as opposed to window.onerror = function... which gets the data parts, such as message & lineNumber as opposed to the real error.

This is basically how I'm using it:

    window.addEventListener('error', function (event) {
      if (event.error.hasBeenCaught !== undefined){
        return false
      }
      event.error.hasBeenCaught = true
      // ... your useful code here
    })

If this is called with the same error twice it will exit before getting to your useful code, only executing the useful code once per error.

It turns out that the object is identical in all calls, you could set up something to match them exactly, or you could just attach a custom attribute to the error...

I'm getting this error and they are different error instances. Both exactly the same message and trace but they aren't referentially equal.

I'm getting this error and they are different error instances.

Which browser & os is that on? I've only tested this on chrome for mac

Have the same issue. I use the onError handler to prompt Snackbar warning when a networkError occurs. This issue results in duplicate Snackbar warnings.

I configured errorPolicy for "all" in Query and solved this problem.
It worked for me.

<Query query={MY_QUERY}
  fetchPolicy="network-only"
  errorPolicy="all"> 
    ... 
</Query>

@rodrigokieffer
What language is that? Can you tell what that compiles in to in terms of JS & HTML?

@cameron-brown-future
My application is in React.js, and it's a Query tag from ApolloClient for React.
When I set attribute errorPolicy to "all" the application didn't call the error handler twice anymore.

```react.js

{({ data, loading, error }) => {
if (loading) {
return ()
}

if (error) {
  SHOW_YOUR_MESSAGE_HERE
  return null
}

return (<YourComponent />)

}}

```

For me, onError is being called twice when using "network-only" but it's being called once for "cache-and-network"

I believe this code from apollo-client is partially to blame, since ObservableQuery passes onError to the error property of the observer and to the catch handler of the promise, so if the first fetchQuery call fails on a network error, both places will catch the error.

We _could_ just put a simple if (this.lastError !== error) around this line to stop ObservableQuery from reporting the same error twice, but this won't work. This is because react-apollo resets the ObservableQuery.lastError property after it receives an error, which means the next time an error comes in, lastError will always be undefined.

I would very much like to fix this since it's an unnecessary annoyance. 馃檪

As suggested in #3331 lodash's debounce could be a way to work around this

Thanks for the info @jasonpaulos - very helpful!

Was this page helpful?
0 / 5 - 0 ratings