Оптимистический ответ Apollo GraphQL и проблема с обновлением магазина

У меня возникла небольшая проблема при обновлении пользовательского интерфейса после мутации с использованием оптимистичного ответа и изменения кеша 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, я могу без проблем добавлять продукты, которые уже существуют. Проблема в том, что если я пытаюсь добавить продукт, которого еще нет в элементах, при визуализации отдельного компонента пользователь не возвращается из запроса, и я получаю неопределенную ошибку, связанную с подсчетом элементов в Корзина.

Сейчас это огромный вопрос, и я, возможно, провалился, но, надеюсь, у кого-то есть идеи, как помочь.

Думаю, проблема в том, где вы обновляете продукт. Из вашей схемы кажется, что ваш продукт имеет тип Product в пользовательской корзине, а затем тип JSON в возвращаемом значении мутации. Или ваш элемент корзины пользователя относится к другому типу, нежели SetOrderItemPayload. Убедитесь, что типы точно совпадают, иначе все может пойти не так!

Herku 09.04.2018 01:51
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
1
1 570
1

Ответы 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...

Другие вопросы по теме