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

时间:2018-01-13 16:49:57

标签: javascript graphql apollo react-apollo apollo-client

我有一个带有消息列表的线程,该消息列表使用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方法中,我想乐观地将发送的消息添加到线程消息中以获得更好的UX。没有分页,我知道我会这样做:

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

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

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

2 个答案:

答案 0 :(得分:1)

我会对此有所了解。

如果没有看到突变,我会认为它看起来像下面这样。

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

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

答案 1 :(得分:0)

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

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方法!