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

时间:2018-04-06 17:32:26

标签: javascript reactjs ecmascript-6 graphql apollo

在使用乐观响应和更改apollo缓存进行突变后更新UI时,我遇到了一些问题。我有以下变异:

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将触发updateCartMutation,其金额为1,除非product.id已存在于items中,在这种情况下,它将增加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中从退货中删除产品,我可以添加已经存在但没有问题的产品。那么问题是,如果我尝试添加项目中已经存在的产品,当单独的组件呈现时,用户不会从查询返回,并且我得到与计数相关的未定义错误购物车中的物品。

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

1 个答案:

答案 0 :(得分:0)

所以我想出了一个解决方案,但我仍然希望有更好的解释。在我的变异中,我添加了一个有条件地返回产品的指令,并传递了一个布尔变量,我是否希望产品数据随突变返回(我在添加一个独特的产品时需要,但在更新数量时不需要)存在于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...