React-apollo: After a query has resulted in an error, variable updates that should result in an Apollo QueryResult with no errors do not update the QueryResult's error field in componentWillReceiveProps

Created on 2 Mar 2018  路  8Comments  路  Source: apollographql/react-apollo

Actual Result

  • Component mounts using graphql HOC, with a Query that produces an error
  • Update props so that variables passed to the operation options will now result in a query that succeeds with no errors
  • No network request is made, and inside componentWillReceiveProps, the nextProps.data.error is still defined with the previous error

Intended Result

  • Component mounts using graphql HOC, with a Query that produces an error
  • Update props so that variables passed to the operation options will now result in a query that succeeds with no errors
  • Either a network request is made, or the data is returned from the cache such that nextProps.data.error is no longer defined

How am I testing this?

Good question. I have a basic query that requests a field that always succeeds, and another field that fails when included:

// query.gql
query SomeQuery($shouldFail: Boolean!) {
  successField {
    id
  }
  failField @include(if: $shouldFail) {
    id
  }
}

Now for my components. I have 2 components, one that acts as a parent and determines whether its child shouldFail or not so that the child gets prop updates, which trigger a variable update when the operation options are different.

// Child
class ChildPresentation extends React.Component<Props> { ... }

// Child connected to graphql HOC
const Child = graphql(query, {
  options: (props: Props) => ({
    variables: {
      shouldFail: props.shouldFail,
    },
  }),
})(ChildPresentation);

// Parent
class Parent extends React.Component<ParentProps> {
  public render() {
    return <Child shouldFail={this.state.shouldFail} />;
  }
}

With this set up, you can see how Parent can easily update its state, which causes Child's shouldFail prop to update, which causes the variable inside the operation options to update... and down the rabbit hole.

The rabbit hole

When a GraphQL error comes through, the Observable decides to clean itself up.

https://github.com/zenparsing/zen-observable/blob/e924b1ddcde8aaac157cedb1908b2ad83b20e7bc/zen-observable.js#L184

Which removes it from ObservableQuery's list of observers (emptying it).

https://github.com/apollographql/apollo-client/blob/2.0/packages/apollo-client/src/core/ObservableQuery.ts#L538

Then when the new query variables come in, it follows the path of:

  • GraphQL.componentWillReceiveProps
  • GraphQL.updateQuery (https://github.com/apollographql/react-apollo/blob/apollo-client-2.0/src/graphql.tsx#L220)
  • ObservableQuery.setOptions (https://github.com/apollographql/react-apollo/blob/apollo-client-2.0/src/graphql.tsx#L391)
  • ObservableQuery.setVariables (https://github.com/apollographql/apollo-client/blob/2.0/packages/apollo-client/src/core/ObservableQuery.ts#L396)

Which would normally end up doing the network request, but since earlier the observers list became empty, this ends up being a no-op.

https://github.com/apollographql/apollo-client/blob/2.0/packages/apollo-client/src/core/ObservableQuery.ts#L450

Then there's no recovery from react-apollo's perspective; when the graphql hoc calls subscribeToQuery at the end of componentWillReceiveProps (after the GraphQL.updateQuery above), that's a no-op because from its perspective it thinks it's still subscribed.

https://github.com/apollographql/react-apollo/blob/apollo-client-2.0/src/graphql.tsx#L432


The above was found and documented after some minor digging to see why this was happening.

Repro Environment

I actually don't have an easily accessible repro environment with public data that can be used for this. Though it should be fairly straightforward to set up by following my components above.

Version

has-PR has-reproduction

Most helpful comment

Just created a PR with a test case.

What I observed in my local tests was:

  • first <Query>execution with correct variables works fine
  • as long changing the query variables still causes GraphQL errors
    - apollo sends out network request (as expected)
    - children comps of <Query> get updated with the new errror
    - BUT: the data in the props still contains the old data of the last successful execution
    (seems not to be intended for me, because the changed variables request completely different data)
  • if the variables change back to values of an already cached query result:
    - obviously no network request is sent out for the already cached data
    - BUT: the children of the <Query>component will not be updated (with the valid data as props)
  • changing variables to another set that is valid
    - <Query>is correctly executed and children are rendered with correct props

All 8 comments

Having the exact same problem, except i'm using the Query component from react-apollo. @benjaminsaurusrex were you able to work around this issue?

According to this PR in apollo-client, the issue should be resolved but i'm still getting the same error.

These are the latest packages i'm using:

  • react-apollo: 2.1.9
  • apollo-client: 2.3.7

@benjaminsaurusrex Have you tried using https://www.apollographql.com/docs/react/features/error-handling.html#policies with an error policy to all? This solved the problem of update of components with query errors in our case.

I recently ran into this (using Query from react-apollo also) and can confirm that setting errorPolicy to all indeed works around this bug, however on subsequent queries the failing query no longer returns an error, which seems a little surprising.

Please consider submitting a PR with a unit test reproduction - that's the best way to get attention to this.

2019 and this still seems to be an issue

Just created a PR with a test case.

What I observed in my local tests was:

  • first <Query>execution with correct variables works fine
  • as long changing the query variables still causes GraphQL errors
    - apollo sends out network request (as expected)
    - children comps of <Query> get updated with the new errror
    - BUT: the data in the props still contains the old data of the last successful execution
    (seems not to be intended for me, because the changed variables request completely different data)
  • if the variables change back to values of an already cached query result:
    - obviously no network request is sent out for the already cached data
    - BUT: the children of the <Query>component will not be updated (with the valid data as props)
  • changing variables to another set that is valid
    - <Query>is correctly executed and children are rendered with correct props

Tried out the errorPolicies:

  • The described usecase works ~fine~ somehow better with the policy ignore. It renders the children components in each case.
    BUT: the error is never passed down as renderProp so there is no way of determining why no data arrived
  • The policies none and all end up in causing the this bug.

So setting the policy to ignore is a workaround for now if you want the children to get updated after an error

Was this page helpful?
0 / 5 - 0 ratings