阿波罗图QL乐观响应和更新存储问题



在使用乐观响应和更改 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 默认情况下会触发金额 = 1 的 updateCartMutation,除非项目中已经存在 product.id,在这种情况下,它将金额增加 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 对象,表示有效负载中的产品。

这里是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的退货中删除产品,我可以毫无问题地添加已经存在的产品。 那么问题是,如果我尝试添加项目中尚不存在的产品,当单独的组件呈现时,用户不会从查询返回,并且我收到与购物车中项目计数相关的未定义错误。

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

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

最新更新