Apollo 客户端:在创建过程中进行乐观更新

Apollo client: Making optimistic updates while creation is still in progress

我希望能够在对象仍在创建时对其进行更新。

例如:假设我有一个待办事项列表,我可以在其中添加带有名称的项目。我还希望能够编辑项目的名称。

现在假设连接速度较慢的用户创建了一个项目。在这种情况下,我会触发创建项目突变并乐观地更新我的 UI。效果很好。目前没问题

现在假设由于网络速度慢,创建项目变更需要一些时间。那时,用户决定编辑他们刚刚创建的项目的名称。为了获得理想的体验:

  1. UI 应立即更新为新名称
  2. 新名称最终应该保存在服务器中

我可以通过等待创建突变完成(以便我可以获得项目 ID),然后进行更新名称突变来实现 #2。但这意味着我的 UI 的部分内容将保持不变,直到创建项目突变 returns 和更新名称突变的乐观响应开始。这意味着 #1 将无法实现。

所以我想知道如何使用 Apollo 客户端同时实现#1 和#2。

注意:我不想添加微调器或禁用编辑。我希望应用即使在连接速度较慢的情况下也能响应。

如果您有权访问服务器,您可以执行 upsert 操作,并且可以将所有查询减少到这样一个:

mutation {
  upsertTodoItem(
    where: {
      key: $itemKey # Some unique key generated on client
    }
    update: {
      listId: $listId
      text: $itemText
    }
    create: {
      key: $itemKey
      listId: $listId
      text: $itemText
    }
  ) {
    id
    key
  }
}

因此您将拥有一系列相同的突变,仅在变量上有所不同。相应地,可以针对这一突变配置乐观的响应。在服务器上,您需要检查是否已经存在带有 key 的项目,并分别创建或更新项目。

此外,您可能希望使用 apollo-link-debounce 来减少用户输入时的请求数量。

我认为实现预期效果的最简单方法是实际放弃乐观更新,转而自行管理组件状态。我现在没有足够的带宽写出一个完整的例子,但是你的基本组件结构应该是这样的:

<ApolloConsumer>
  {(client) => (
    <Mutation mutation={CREATE_MUTATION}>
      {(create) => (
        <Mutation mutation={EDIT_MUTATION}>
          {(edit) => (
            <Form />
          )}
        </Mutation>        
      )}
    </Mutation>
  )}
</ApolloConsumer>

假设我们只处理一个字段 -- name。您的 Form 组件将以

的初始状态开始
{ name: '', created: null, updates: null }

提交后,表单会执行以下操作:

onCreate () {
  this.props.create({ variables: { name: this.state.name } })
    .then(({ data, errors }) => {
      // handle errors whichever way
      this.setState({ created: data.created })
      if (this.state.updates) {
        const id = data.created.id
        this.props.update({ variables: { ...this.state.updates, id } })
      }
    })
    .catch(errorHandler)
}

然后编辑逻辑看起来像这样:

onEdit () {
  if (this.state.created) {
    const id = this.state.created.id
    this.props.update({ variables: { name: this.state.name, id } })
      .then(({ data, errors }) => {
        this.setState({ updates: null })
      })
      .catch(errorHandler)
  } else {
    this.setState({ updates: { name: this.state.name } })
  }
}

实际上,您的编辑变更要么在用户提交时立即触发(因为我们已经从我们的创建变更中收到响应)...或者用户所做的更改被持久化,然后在创建变更后发送完成。

这是一个非常粗略的示例,但应该可以让您了解如何处理此类情况。最大的缺点是您的组件状态有可能与缓存不同步——您需要确保正确处理错误以防止这种情况发生。

这也意味着如果您想将此表单用于 只是 编辑,您需要从缓存中获取数据,然后使用它来填充您的初始状态(即上例中的 this.state.created)。您可以为此使用 Query 组件,只需确保在获得 Query 组件提供的 data 道具之前不要渲染实际的 Form 组件。