简体   繁体   English

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

[英]Apollo GraphQL Optimistic Response and updating the store issue

I'm running into a bit of a problem when updating the UI after a mutation using an Optimistic Response and altering the apollo cache. 使用乐观响应进行突变后更改UI并更改阿波罗缓存时,我遇到了一个问题。 I have the following Mutation: 我有以下突变:

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

And the following Query: 和以下查询:

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

In my React component I have a button which fires the following method: 在我的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)
      }
    }
  })
}

Here's what I expect. 这就是我的期望。 addToCart will by default fire the updateCartMutation with an amount = 1 unless the product.id already exists in items, in which case it will increment amount by 1. The optimistic response is mocked out and the update should read the USER_CART_QUERY from the store. 除非在产品中已经存在product.id,否则addToCart将默认以数量= 1触发updateCartMutation,在这种情况下,它将以1递增数量。乐观响应被模拟,更新应从商店读取USER_CART_QUERY。 If the item already exists in the query we splice out the original item from the store and replace it with the setOrderItem payload. 如果该项目已经存在于查询中,我们将从商店中拼接出原始项目,然后将其替换为setOrderItem有效负载。 If the item doesn't exist, we push the payload to items. 如果物品不存在,我们将有效载荷推入物品。 Finally we write the query to the store. 最后,我们将查询写入商店。

When I'm adding a new item to the cart, everything works perfectly. 当我向购物车中添加新物品时,一切工作正常。 I notice that the optimistic response is mocked and the cart query is read from the store by update. 我注意到,乐观响应是模拟的,购物车查询是通过更新从商店中读取的。 I successfully push the payload into items and immediately my UI is updated. 我成功将有效负载推入项目,并立即更新了UI。 Shortly after that update is called again with the response from the server. 此后不久,服务器会响应再次调用该更新。 This is pushed into items, written to the store, and everything works as expected. 将其推送到项目中,写入商店,一切都会按预期进行。

The first time that I try to add an item which already exists in items, it seems to work fine. 第一次尝试添加已存在于项目中的项目时,它似乎工作正常。 Also, if I attempt after a fresh refresh, everything works fine. 另外,如果我在尝试重新刷新后一切正常。 The second time I try to add, I get an error. 第二次尝试添加时,出现错误。 What I'm noticing is that the optimistic response is mocked out, spliced into items and then written to the store, but when update is called again with the update from the server, I get the following error: 我要注意的是,乐观响应已被模拟出来,拼接成项目,然后写入到存储中,但是当使用服务器的更新再次调用update时,出现以下错误:

"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." “在查询中遇到了一个子选择,但是商店没有对象引用。除非您有直接操纵商店的自定义代码,否则在正常使用期间这永远不会发生;请提出问题。”

The error is thrown in readFromtStore.js: 在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.");
    }
  }

At this point, idValue is a json object representing the Product from the payload. 此时,idValue是一个json对象,表示有效负载中的Product。

Here is setOrderItem.graphql 这是setOrderItem.graphql

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

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

And in setOrderItem.ts I'm returning product from 在setOrderItem.ts中,我从

product: Product(id: $productId) {
  id
  name
  price
  description
  imageUrl
  __typename
}

The last thing I want to point out is that if I remove product from the return in the UPDATE_CART_MUTATION, I can add products that already exist with no issues. 我要指出的最后一件事是,如果我从UPDATE_CART_MUTATION的退货中删除产品,则可以添加已经存在的产品而没有任何问题。 The problem then is that if I try to add a product that doesn't already exist in items, when a separate component renders, the User isn't returned from the query and I get an undefined error related to a count of the items in the cart. 然后的问题是,如果我尝试添加商品中尚不存在的产品,则在渲染单独的组件时,不会从查询中返回用户,并且会收到与其中的商品计数相关的未定义错误购物车。

This question is huge now and I may be down a hole but hopefully someone will have some idea how to help. 这个问题现在很大,我可能会陷入困境,但希望有人会有所帮助。

So I figured out a solution, but I'm still hoping for a better explanation. 所以我想出了一个解决方案,但我仍然希望有一个更好的解释。 In my mutation, I added a directive on returning product conditionally and passed in a boolean variable whether I wanted the product data to return with the mutation (Which I need when adding a unique product, but don't want when updating the amount of a product which exists in the items array. 在我的突变中,我添加了一条关于有条件地返回产品的指令,并传递了一个布尔变量,无论我是否希望产品数据随突变返回(在添加唯一产品时需要,但在更新商品数量时不需要项目数组中存在的产品。

The mutation looks like: 突变看起来像:

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

and my addToCart function: 和我的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...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM