最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to update a paginated list after a mutation? - Stack Overflow

programmeradmin2浏览0评论

I have a thread with a list of messages, which is fetched with a GET_THREAD_MESSAGES query. That query is paginated, and depending on if a user has seen the thread before or not might load the first page, last page or only the new messages. (i.e. any of first/after/before/last could be passed with any values)

thread(id: "asdf") {
  messageConnection(after: $after, first: $first, before: $before, last: $last) {
    edges {
      cursor
      node { ...messageInfo }
    }
  }
}

Now I have a sendMessage mutation, which I call and then in the update method of that mutation I want to optimistically add that sent message to the threads messages for a nicer UX. Without pagination, I know I would do that something like:

const data = store.readQuery({
  query: GET_THREAD_MESSAGES,
  variables: { id: message.threadId }
})

data.messageConnection.edges.push(newMessageEdge);

store.writeQuery({
  query: GET_THREAD_MESSAGES,
  variables: { id: message.threadId },
  data,
})

Unfortunately, since I know have pagination the store.readQuery call throws an error saying “It can’t find the field messageConnection of that thread” because the field is now something like messageConnection({ after: 'jfds1223asdhfl', first: 50, before: null, last: null }). The Apollo docs say that one should use the @connection directive in the query to work around that. I've tried to update the query to look something like this:

thread(id: "asdf") {
  messageConnection(...) @connection(key: "messageConnection") {
    edges {
      cursor
      node { ...messageInfo }
    }
  }
}

Unfortunately, when I use that the optimistic update is returned and shown correctly, but as soon as the server returns the actual message that was stored I get an error saying "Missing field cursor in { node: { id: '...', timestamp: '...'", because obviously the message that the server returns is not a MessageConnectionEdge, it's just the node, and thusly doesn't have a cursor field.

How can I tell Apollo to only replace the node of the optimistic response, not the entire edge? Is there another way to work around the original issue maybe?

I have a thread with a list of messages, which is fetched with a GET_THREAD_MESSAGES query. That query is paginated, and depending on if a user has seen the thread before or not might load the first page, last page or only the new messages. (i.e. any of first/after/before/last could be passed with any values)

thread(id: "asdf") {
  messageConnection(after: $after, first: $first, before: $before, last: $last) {
    edges {
      cursor
      node { ...messageInfo }
    }
  }
}

Now I have a sendMessage mutation, which I call and then in the update method of that mutation I want to optimistically add that sent message to the threads messages for a nicer UX. Without pagination, I know I would do that something like:

const data = store.readQuery({
  query: GET_THREAD_MESSAGES,
  variables: { id: message.threadId }
})

data.messageConnection.edges.push(newMessageEdge);

store.writeQuery({
  query: GET_THREAD_MESSAGES,
  variables: { id: message.threadId },
  data,
})

Unfortunately, since I know have pagination the store.readQuery call throws an error saying “It can’t find the field messageConnection of that thread” because the field is now something like messageConnection({ after: 'jfds1223asdhfl', first: 50, before: null, last: null }). The Apollo docs say that one should use the @connection directive in the query to work around that. I've tried to update the query to look something like this:

thread(id: "asdf") {
  messageConnection(...) @connection(key: "messageConnection") {
    edges {
      cursor
      node { ...messageInfo }
    }
  }
}

Unfortunately, when I use that the optimistic update is returned and shown correctly, but as soon as the server returns the actual message that was stored I get an error saying "Missing field cursor in { node: { id: '...', timestamp: '...'", because obviously the message that the server returns is not a MessageConnectionEdge, it's just the node, and thusly doesn't have a cursor field.

How can I tell Apollo to only replace the node of the optimistic response, not the entire edge? Is there another way to work around the original issue maybe?

Share Improve this question edited Sep 4, 2018 at 10:54 Joshua 3,2063 gold badges26 silver badges41 bronze badges asked Jan 13, 2018 at 16:49 mxstbrmxstbr 11.5k4 gold badges41 silver badges38 bronze badges 1
  • Can we see the mutation too? – imranolas Commented Jan 15, 2018 at 11:58
Add a ment  | 

2 Answers 2

Reset to default 3

Phew, I finally fixed this. The problem turned out not to be in the mutation at all, it was in the subscription that was running parallel for new messages in the thread. (facepalm)

TL;DR: If you have a subscription on the same data as a mutation is changing, make sure to include the same fields in both update methods!

The subscription method looked like this:

subscribeToNewMessages: () => {
  return props.data.subscribeToMore({
    document: subscribeToNewMessages,
    variables: {
      thread: props.ownProps.id,
    },
    updateQuery: (prev, { subscriptionData }) => {
      const newMessage = subscriptionData.data.messageAdded;
      return Object.assign({}, prev, {
        ...prev,
        thread: {
          ...prev.thread,
          messageConnection: {
            ...prev.thread.messageConnection,
            edges: [
              ...prev.thread.messageConnection.edges,
              { node: newMessage, __typename: 'ThreadMessageEdge' },
            ],
          },
        },
      });
    },
  });
},

If you have good eyes, you'll immediately spot the issue: The inserted edge doesn't provide a cursor—which is exactly what Apollo Client is telling us in the warning! The fix was to add a cursor to the inserted edge:

{ node: newMessage, cursor: newMessage.id, __typename: 'ThreadMessageEdge' }

Hope this helps somebody else running into this warning, make sure to triple check both the subscription and the mutation update methods!

I'll have a crack at this.

Without seeing the mutation I'll presume that it looks something like the following.

mutation NewMessage($message: String!, $threadId: ID!) {
  sendMessage(message: $message, threadId: $threadId) {
    ...messageInfo
  }
}

If that is the case, it's potentially unreliable to infer where in the connection the message should go. Since cursors are opaque strings we can't be certain that this message should indeed e after the latest message.

Instead I'd try something like the following.

mutation NewMessage(message: String!, threadId: ID!, $after: Cursor, first: Int) {

  sendMessage(message: $message, threadId: $threadId) {
    messageConnection(
      after: $after,
      first: $first,
      before: null,
      last: null
    ) @connection(key: "messageConnection") {
      edges {
        cursor
        node { ...messageInfo }
      }
    }
  }
}

This connection should include the new message and any others that have been added since.

Here is the official documentation on the @connection directive: https://www.apollographql./docs/react/advanced/caching/#the-connection-directive

发布评论

评论列表(0)

  1. 暂无评论