У меня возникла небольшая проблема при обновлении пользовательского интерфейса после мутации с использованием оптимистичного ответа и изменения кеша 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.info(err)
}
}
})
}
Вот чего я ожидал. addToCart по умолчанию запускает updateCartMutation с количеством = 1, если product.id уже не существует в элементах, и в этом случае он увеличит сумму на 1. Оптимистичный ответ имитируется, и обновление должно читать USER_CART_QUERY из магазина. Если элемент уже существует в запросе, мы выделяем исходный элемент из магазина и заменяем его полезными данными setOrderItem. Если элемент не существует, мы отправляем полезную нагрузку в элементы. Напоследок пишем запрос в магазин.
Когда я добавляю новый товар в корзину, все работает отлично. Я замечаю, что оптимистичный ответ высмеян, а запрос корзины считывается из магазина путем обновления. Я успешно помещаю полезную нагрузку в элементы, и мой пользовательский интерфейс немедленно обновляется. Вскоре после этого обновление вызывается снова с ответом от сервера. Это помещается в элементы, записывается в магазин, и все работает, как ожидалось.
В первый раз, когда я пытаюсь добавить элемент, который уже существует в элементах, кажется, что он работает нормально. Кроме того, если я попытаюсь выполнить новое обновление, все будет работать нормально. Второй раз пытаюсь добавить, выдает ошибку. Я замечаю, что оптимистичный ответ имитируется, объединяется в элементы и затем записывается в магазин, но когда обновление вызывается снова с обновлением с сервера, я получаю следующую ошибку:
«Обнаружен подвыбор в запросе, но в магазине нет ссылки на объект. Этого не должно происходить при нормальном использовании, если только у вас нет специального кода, который напрямую управляет магазином; сообщите о проблеме».
Ошибка возникает в 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, я могу без проблем добавлять продукты, которые уже существуют. Проблема в том, что если я пытаюсь добавить продукт, которого еще нет в элементах, при визуализации отдельного компонента пользователь не возвращается из запроса, и я получаю неопределенную ошибку, связанную с подсчетом элементов в Корзина.
Сейчас это огромный вопрос, и я, возможно, провалился, но, надеюсь, у кого-то есть идеи, как помочь.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Итак, я нашел решение, но все еще надеюсь на лучшее объяснение. В своей мутации я добавил директиву об условном возврате продукта и передал логическую переменную, хочу ли я, чтобы данные продукта возвращались с мутацией (что мне нужно при добавлении уникального продукта, но не хочу при обновлении количества продукт, который существует в массиве 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...
Думаю, проблема в том, где вы обновляете продукт. Из вашей схемы кажется, что ваш продукт имеет тип
Productв пользовательской корзине, а затем тип JSON в возвращаемом значении мутации. Или ваш элемент корзины пользователя относится к другому типу, нежелиSetOrderItemPayload. Убедитесь, что типы точно совпадают, иначе все может пойти не так!