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

Posted

技术标签:

【中文标题】Apollo 客户端:在创建过程中进行乐观更新【英文标题】:Apollo client: Making optimistic updates while creation is still in progress 【发布时间】:2019-04-10 08:56:15 【问题描述】:

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

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

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

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

    用户界面应立即更新为新名称 新名称最终应保留在服务器中

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

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

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

【问题讨论】:

在创建的情况下进行乐观更新。如果有更新,请保持突变准备就绪,并在创建操作完成并且您拥有 id 后在后台执行此操作 【参考方案1】:

如果您可以访问服务器,则可以实现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 来减少用户键入时的请求数。

【讨论】:

【参考方案2】:

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

<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 组件。

【讨论】:

以上是关于Apollo 客户端:在创建过程中进行乐观更新的主要内容,如果未能解决你的问题,请参考以下文章

Apollo 客户端递归突变

Apollo 客户端乐观响应如何工作

在 Apollo v3 React 客户端 fe-retching 不同查询的情况下如何实现乐观 UI

突变后使用订阅和更新创建重复节点 - 使用 Apollo 客户端

Apollo GraphQL 乐观响应和更新商店问题

Apollo 客户端:缓存更新后组件不呈现(反应变量)