Relay: [Modern] connection is undefined when retrieving it from `ConnectionHandler`

Created on 24 Jun 2017  Â·  9Comments  Â·  Source: facebook/relay

I'm trying to update the cache after a mutation using the updater and optimisticUpdater but it doesn't quite work. Basically, I have a Link type with a votes connection - here's the relevant part of my schema:

type Link implements Node {
  createdAt: DateTime!
  description: String!
  id: ID!
  postedBy(filter: UserFilter): User
  url: String!
  votes(filter: VoteFilter, orderBy: VoteOrderBy, skip: Int, after: String, before: String, first: Int, last: Int): VoteConnection
}

type Vote implements Node {
  createdAt: DateTime!
  id: ID!
  link(filter: LinkFilter): Link!
  updatedAt: DateTime!
  user(filter: UserFilter): User!
}

# A connection to a list of items.
type VoteConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo

  # A list of edges.
  edges: [VoteEdge]

  # Count of filtered result set without considering pagination arguments
  count: Int!
}

# An edge in a connection.
type VoteEdge {
  # The item at the end of the edge.
  node: Vote

  # A cursor for use in pagination.
  cursor: String
}

Here's the code for my Link component where I request the votes in a fragment:

class Link extends Component {

  render() {
    const userId = localStorage.getItem(GC_USER_ID)
    return (
      <div>
        {userId && <div onClick={() => this._voteForLink()}>â–²</div>}
        <div>{this.props.link.description} ({this.props.link.url})</div>
        <div>{this.props.link.votes.edges.length} votes | by {this.props.link.postedBy ? this.props.link.postedBy.name : 'Unknown'} {this.props.link.createdAt}</div>
      </div>
    )
  }

  _voteForLink = () => {
    const userId = localStorage.getItem(GC_USER_ID)
    const linkId = this.props.link.id
    CreateVoteMutation(userId, linkId, this.props.viewer.id)
  }

}

export default createFragmentContainer(Link, graphql`
  fragment Link_viewer on Viewer {
    id
  }
  fragment Link_link on Link {
    id
    description
    url
    createdAt
    postedBy {
      id
      name
    }
    votes(last: 1000, orderBy: createdAt_DESC) @connection(key: "Link_votes", filters: []) {
      edges {
        node {
          id
          user {
            id
          }
        }
      }
    }
  }
`)

Finally, this is the CreateVoteMutation with the updater:

const mutation = graphql`
  mutation CreateVoteMutation($input: CreateVoteInput!) {
    createVote(input: $input) {
      vote {
        id
        link {
          id
        }
        user {
          id
        }
      }
    }
  }
`

export default (userId, linkId, viewerId) => {
  const variables = {
    input: {
      userId,
      linkId,
      clientMutationId: ""
    },
  }

  commitMutation(
    environment,
    {
      mutation,
      variables,
      updater: (proxyStore) => {
        const createVoteField = proxyStore.getRootField('createVote')
        const newVote = createVoteField.getLinkedRecord('vote')

        const viewerProxy = proxyStore.get(viewerId)
        const connection = ConnectionHandler.getConnection(viewerProxy, 'Link_votes')
        // `connection` is undefined, so the `newVote` doesn't get inserted
        if (connection) {
          ConnectionHandler.insertEdgeAfter(connection, newVote)
        }
      },
      onError: err => console.error(err),
    },
  )
}

The call to ConnectionHandler.getConnection(viewerProxy, 'Link_votes') only returns undefined, so the newVote doesn't actually get inserted.

Does anyone see what I'm doing wrong?

Most helpful comment

First, When doing mutation with relay modern. After mutation activity ended we can dump our store with RelayStoreProxyDebugger. Its not in docs yet but I know this "trick" 😄 from some comment in another relay issue. It really helpful.

import storeDebugger from 'relay-runtime/lib/RelayStoreProxyDebugger'
//...
  commitMutation(
    environment,
    {
      mutation,
      variables,
      updater: (proxyStore) => {
        //...
        //...
        if (connection) {
          ConnectionHandler.insertEdgeAfter(connection, newVote)
        }
        //dump the store
        storeDebugger.dump(proxyStore)
      },
      //...
    },
  )

second, since you querying your VoteConnection with last and orderBy arguments, relay connection directive need to know that. so the correct connection directive filters is

votes(last: 1000, orderBy: createdAt_DESC) 
@connection(key: "Link_votes", filters: ["last","orderBy"])

third, since your voteconnection argument value is hardcoded one. the connection handler need to know the value of argument. so it can match in the store

const connection = ConnectionHandler.getConnection(viewerProxy, 'Link_votes', {
  last: 1000,
  orderBy: 'createdAt_DESC'
})

All 9 comments

In the updater function, you could try adding the following code to inspect what data is cached (also under which key)

require('RelayStoreProxyDebugger').dump(proxyStore)

First, When doing mutation with relay modern. After mutation activity ended we can dump our store with RelayStoreProxyDebugger. Its not in docs yet but I know this "trick" 😄 from some comment in another relay issue. It really helpful.

import storeDebugger from 'relay-runtime/lib/RelayStoreProxyDebugger'
//...
  commitMutation(
    environment,
    {
      mutation,
      variables,
      updater: (proxyStore) => {
        //...
        //...
        if (connection) {
          ConnectionHandler.insertEdgeAfter(connection, newVote)
        }
        //dump the store
        storeDebugger.dump(proxyStore)
      },
      //...
    },
  )

second, since you querying your VoteConnection with last and orderBy arguments, relay connection directive need to know that. so the correct connection directive filters is

votes(last: 1000, orderBy: createdAt_DESC) 
@connection(key: "Link_votes", filters: ["last","orderBy"])

third, since your voteconnection argument value is hardcoded one. the connection handler need to know the value of argument. so it can match in the store

const connection = ConnectionHandler.getConnection(viewerProxy, 'Link_votes', {
  last: 1000,
  orderBy: 'createdAt_DESC'
})

I worked around the issue in a different way.

@nikolasburk how?

@saudpunjwani101 Check the subscriptions chapter of the React & Relay tutorial on How to GraphQL. It's all in there!

Subscription is a good idea but it's way too much work in my case scenario. User just adds an item and I want the list updated. I'm not sure if I require real time updates for that. Any other suggestions before I go that deep?

@nosykretts dumbing the store gives an error,

console.groupCollapsed is not a function

hello everyone,

I got same error..
https://github.com/facebook/relay/issues/2163

please give me solution for this.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

johntran picture johntran  Â·  3Comments

fedbalves picture fedbalves  Â·  3Comments

rayronvictor picture rayronvictor  Â·  3Comments

staylor picture staylor  Â·  3Comments

bondanherumurti picture bondanherumurti  Â·  3Comments