This is a bit of a head scratcher, but it seems like in certain cases errors in the render methods of child components are being swallowed by react-apollo, showing up in the error.networkErrorproperty of the data object. Here's the best approximation of code to reproduce:
const ChildComponent = ({ user }) => {
return (
<div>{ user.property.doesntExist }</div>
)
}
const Query = gql`
query UserQuery () {
user {
fullName
}
}
`
@graphQl(Query)
class SmartComponent extends Component {
render() {
const { loading, user } = this.props.data
if (loading) {
return <Loading />
} else {
return <ChildComponent user={user}
}
}
}
Unfortunately, the type error of accessing a property on undefined (user.property.doesntExist) in the child property is swallowed by apollo. You can find the error by logging this.props.data in the parent component, and you'll see this:

I'm not 100% sure if this is happening specifically in my use case or if there's something more general here. I think it's 100% not correct for an error in a child component to wind up being handled by Apollo's network error handling, and would expect that an error thrown by downstream (child) components would be thrown as usual
In graphql.tsx (here), it seems the following thing that is warned about in a comment is actually happening...
...
/*
Since `setState()` can throw an error if the child had a render error,
we can't use the `error` part of the query subscription. If we do, all children
errors are captured as NetworkErrors which isn't true / helpful.
Instead, we subscribe to the store for network errors and re-render that way
*/
this.querySubscription = this.queryObservable.subscribe({ next, error: handleError });
}
OK. Think I have something of a handle on what's happening here.
In apollo-client/src/core/QueryManager.tsx (code truncated)
public queryListenerForObserver(
...
if (observer.next) {
...
try {
observer.next(this.transformResult(resultFromStore))
}
When the next result (this.transformResult(resultFromStore)) is returned from the store, it's passed as an argument into the observer's next method, which is defined below in react-apollo
In react-apollo/src/graphql.tsx (code truncated)
subscribeToQuery(props): boolean {
...
const next = (results: any) => {
...
this.forceRenderChildren()
The next method provided to the ObservableQuery observer is invoked when the result of the query is received. This triggers an invocation of this.forceRenderChildren, which then leads to the typeError when the child component is rendered. This error is handled by the first queryListenerForObserver method, which calls observer.next in a try/catch block:
In apollo-client/src/core/QueryManager.tsx (code truncated)
public queryListenerForObserver(
...
if (observer.next) {
...
try {
observer.next(this.transformResult(resultFromStore))
} catch (error) {
observer.error(new ApolloError({
networkError: error,
}));
The error method declared in react-apollo has conditional logic for handling errors of typeApolloError, but any error anywhere in the render chain is getting cast to an ApolloError, which means that Apollo is swallowing them
In react-apollo/src/graphql.tsx (code truncated)
const handleError = (error) => {
if (error instanceof ApolloError) return next({ error });
throw error;
};
I'm not exactly sure what to do about this, but I'm going to keep poking around at the internals to learn more about what the intended behavior here is. Anyway, having child component render errors get cast to an ApolloError (and a networkError property at that!) is kind of a deal breaker, so I'd really like to figure this one out.
This is an issue with apollo-client. I've submitted a PR resolving it
Resolved in apollostack/apollo-client#910. Closing
Most helpful comment
This is an issue with
apollo-client. I've submitted a PR resolving ithttps://github.com/apollostack/apollo-client/pull/910