如何在突变后更新分页列表?

How to update a paginated list after a mutation?

我有一个包含消息列表的线程,它是通过 GET_THREAD_MESSAGES 查询获取的。该查询是分页的,根据用户之前是否看过该线程,可能会加载第一页、最后一页或仅加载新消息。 (即任何 first/after/before/last 都可以传递任何值)

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

现在我有一个 sendMessage 突变,我调用它然后在该突变的 update 方法中我想乐观地将发送的消息添加到线程消息以获得更好的用户体验。没有分页,我知道我会这样做:

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,
})

不幸的是,因为我知道有分页,所以 store.readQuery 调用会抛出一个错误,提示“找不到该线程的字段 messageConnection”,因为该字段现在类似于 messageConnection({ after: 'jfds1223asdhfl', first: 50, before: null, last: null }). Apollo docs 表示应该在查询中使用 @connection 指令来解决这个问题。我尝试将查询更新为如下所示:

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

不幸的是,当我使用乐观更新返回并正确显示时,但是一旦服务器 returns 存储了实际消息,我就会收到一条错误消息 "Missing field cursor in { node: { id: '...', timestamp: '...'",因为很明显服务器 returns 不是 MessageConnectionEdge 的消息,它只是节点,因此没有游标字段。

如何告诉Apollo只替换乐观响应的节点,而不是整条边?是否有另一种方法可以解决原始问题?

我会解决这个问题的。

在没有看到突变的情况下,我会假设它看起来像下面这样。

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

如果是这种情况,则推断消息应该发送到连接的哪个位置可能不可靠。由于游标是不透明的字符串,我们不能确定这条消息确实应该出现在最新消息之后。

相反,我会尝试下面的方法。

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 }
      }
    }
  }
}

此连接应包括新消息和此后添加的任何其他消息。

这里是关于 @connection 指令的官方文档:https://www.apollographql.com/docs/react/advanced/caching/#the-connection-directive

哇,我终于解决了这个问题。结果证明问题根本不在突变中,而是在订阅中,线程中的新消息是 运行 并行的。 (捂脸)

TL;DR:如果您订阅了相同的数据,因为突变正在发生变化,请确保在两种更新方法中包含相同的字段!

订阅方法如下所示:

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' },
            ],
          },
        },
      });
    },
  });
},

如果你有好眼力,你会立即发现问题:插入的边缘不提供光标——这正是 Apollo Client 在警告中告诉我们的!解决方法是将光标添加到插入的边缘:

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

希望这有助于其他人 运行 了解此警告,确保对订阅和变更 update 方法进行三重检查!