Search code examples
javascriptreactjsreduxgraphqlapollostack

How to update a separate components props after performing a mutation with Apollo


With redux, when the state changes it updates any components props that is connected to the store with mapStateToProps. However with Apollo when performing a mutation, any component that is using the same data receive the new props.

I understand this is expected behaviour because Apollo doesn't know that the data sets are the same. Here's an example of what I'm getting at:

const query = gql`query { me { username } }`

@graphql(query)
class Header extends React.Component {
  render () {
    return <h1>{this.props.data.me.username}</h1>
  }
}

const mutation = gql`mutation updateAccount($username: String!) {
  updateAccount(username: $username) {
    me {
      username
    }
  }
}`
@graphql(mutation)
class Edit extends React.Component {
  render () {
    # ...
  }
  onSubmit(e) {
    e.preventDefault()
    this.props.mutate({variables: {username: this.state.username})
  }
}

The Header component renders out the username, where as the Edit component updates the username. I want to re-render Header when username changes. I'm not sure how to do this without polling the query.


Solution

  • Apollo keeps an internal cache of queries and is able to normalize the result sets with dataIdFromObject using the internal cache kept in the redux store.

    In order to use the internal cache you must set dataIdFromObject when initializing your client:

    const client = new ApolloClient({
      dataIdFromObject: o => o.id
    })
    

    Then when performing a mutation, ensure the id is returned in the return type:

    const mutation = gql`mutation updateAccount($username: String!) {
      updateAccount(username: $username) {
        me {
          id
          username
        }
      }
    }`
    

    The query should also contain the id:

    const query = gql`query { me { id username } }`
    

    Apollo will recognise that the id is identical between the query and the return type of the mutation and update the query data accordingly.

    I'm using Global ID's in my GraphQL API so the id is always unique across types but if you are using auto-increment ids or they are not unique then you can use __typename from the object:

    const client = new ApolloClient({
      dataIdFromObject: o => `${o.__typename}:${o.id},`
    })