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

Posted

技术标签:

【中文标题】Apollo GraphQL 乐观响应和更新商店问题【英文标题】:Apollo GraphQL Optimistic Response and updating the store issue 【发布时间】:2018-09-16 19:14:18 【问题描述】:

在使用 Optimistic Response 进行突变后更新 UI 并更改 apollo 缓存时,我遇到了一些问题。我有以下突变:

mutation updateCart($basketId: ID!, $productId: ID!, $amount: Int!) 
  setOrderItem(
    basketId: $basketId,
    productId: $productId,
    amount: $amount
  ) 
    id
    amount
    product
  

还有以下查询:

query cart($userId: ID!) 
  User(id: $userId) 
    id
    basket 
      id
      items 
        id
        amount
        product 
          id
          name
          price
          imageUrl
          description
        
      
    
  

在我的 React 组件中,我有一个触发以下方法的按钮:

addToCart = async (product) => 
  const basketId = this.props.basketId
  const items = this.props.userCartQuery.User.basket.items
  let amount = 1

  for (const item of items) 
    if(item.product.id === product.id) 
      amount = item.amount + 1
    
  

  await this.props.updateCartMutation(
    variables: 
      basketId: basketId,
      productId: product.id,
      amount: amount
    ,
    optimisticResponse: 
      __typename: 'Mutation',
      setOrderItem: 
        id: Math.round(Math.random() * -1000000),
        amount: amount,
        product: product,
        __typename: 'SetOrderItemPayload'
      
    ,
    update: (store,  data:  setOrderItem  ) => 
      try 
        const data = store.readQuery(
          query: USER_CART_QUERY,
          variables: 
            userId: this.props.userId,
          
        )
        let replace = false
        for (const key in items) 
          if (items[key].product.id === product.id) 
            data.User.basket.items.splice(key, 1, setOrderItem)
            replace = true
          
        
        if (!replace) 
          data.User.basket.items.push(setOrderItem)
        
        store.writeQuery( query: USER_CART_QUERY, data )
      
      catch(err) 
        console.log(err)
      
    
  )

这就是我所期望的。默认情况下,addToCart 将使用 amount = 1 触发 updateCartMutation,除非 product.id 已经存在于项目中,在这种情况下,它将增加 amount 1。乐观响应被模拟出来,更新应该从商店中读取 USER_CART_QUERY。如果该项目已经存在于查询中,我们从存储中拼接出原始项目并将其替换为 setOrderItem 有效负载。如果项目不存在,我们将有效负载推送到项目。最后我们将查询写入存储。

当我将新商品添加到购物车时,一切正常。我注意到乐观响应被嘲笑,并且购物车查询是通过更新从商店中读取的。我成功地将有效负载推送到项目中,并立即更新了我的 UI。不久之后,再次使用来自服务器的响应调用该更新。这被推送到项目中,写入商店,一切都按预期工作。

我第一次尝试添加项目中已经存在的项目时,它似乎工作正常。此外,如果我在重新刷新后尝试,一切正常。第二次尝试添加时,出现错误。我注意到的是,乐观响应被模拟出来,拼接到项目中,然后写入存储,但是当从服务器更新再次调用更新时,我收到以下错误:

“在查询中遇到子选择,但商店没有对象引用。这在正常使用期间绝不应该发生,除非您有直接操作商店的自定义代码;请提出问题。”

readFromtStore.js 中抛出错误:

  export function assertIdValue(idValue) 
    if (!isIdValue(idValue)) 
        throw new Error("Encountered a sub-selection on the query, but the store doesn't have an object reference. This should never happen during normal use unless you have custom code that is directly manipulating the store; please file an issue.");
    
  

此时,idValue 是一个 json 对象,表示来自有效负载的 Product。

这里是 setOrderItem.graphql

type SetOrderItemPayload 
  id: ID
  amount: Int!
  product: Json


extend type Mutation 
  setOrderItem(
    basketId: ID!
    productId: ID!
    amount: Int!
  ): SetOrderItemPayload!

在 setOrderItem.ts 我从

返回产品
product: Product(id: $productId) 
  id
  name
  price
  description
  imageUrl
  __typename

我要指出的最后一件事是,如果我从 UPDATE_CART_MUTATION 中的退货中删除产品,我可以添加已经存在且没有问题的产品。那么问题是,如果我尝试添加项目中尚不存在的产品,则当呈现单独的组件时,不会从查询中返回用户,并且我会收到与项目计数相关的未定义错误购物车。

这个问题现在很大,我可能陷入困境,但希望有人知道如何提供帮助。

【问题讨论】:

我认为问题在于您更新产品的位置。从您的架构看来,您的产品在用户购物篮中属于 Product 类型,然后在突变返回值中属于 JSON 类型。或者您的用户购物篮中的购物篮项目与SetOrderItemPayload 的类型不同。确保类型完全匹配,否则事情可能会出错! 【参考方案1】:

所以我想出了一个解决方案,但我仍然希望有更好的解释。在我的突变中,我添加了一个关于有条件地返回产品的指令,并传入一个布尔变量是否我希望产品数据随突变返回(添加唯一产品时我需要,但在更新数量时不想要存在于 items 数组中的产品。

突变看起来像:

mutation updateCart($basketId: ID!, $productId: ID!, $amount: Int!, $updateCart: Boolean!) 
    setOrderItem(
      basketId: $basketId,
      productId: $productId,
      amount: $amount
    ) 
      id
      amount
      product @skip(if: $updateCart)
    
  

还有我的 addToCart 函数:

addToCart = async (product) => 
    const basketId = this.props.basketId
    const items = this.props.userCartQuery.User.basket.items
    let amount = 1
    let updateCart = false

    for (const item of items) 
      if(item.product.id === product.id) 
        amount = item.amount + 1
        updateCart = true
      
    

    await this.props.updateCartMutation(
      variables: 
        basketId: basketId,
        productId: product.id,
        amount: amount,
        updateCart: updateCart
      ,
      optimisticResponse: 
        ...Rest of function...

【讨论】:

以上是关于Apollo GraphQL 乐观响应和更新商店问题的主要内容,如果未能解决你的问题,请参考以下文章

Apollo/graphQL:如何将乐观 UI 用于嵌套反应组件的突变?

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

React Apollo - nodejs graphql后端返回异步响应后如何重新获取(更新)数据

如何在 Apollo 中使用乐观 UI 处理删除突变?

GraphQL:无法读取 Apollo 客户端中的响应标头

使用 apollo 捕获来自 graphql 的特定响应