Кеш клиента Apollo работает не так, как я ожидал

Моя проблема:

Я новичок в GraphQL, и я разрабатываю свое первое приложение с полным стеком, используя сервер и клиент Apollo, это простой блог.

На стороне клиента я использую один и тот же запрос на двух разных страницах, но с разными переменными. Запросы запрашивают статью в блоге по идентификатору или слагу, в зависимости от страницы, которую я использую. Таким образом, результат тот же, изменяются только переменные запросов.

Когда я использую запрос на одной странице, я думал, что запрос не будет работать на второй странице из-за кеша Apollo. Но это не то, что происходит. Запрос запускается снова во второй и, конечно же, возвращает мне тот же результат, что и на другой странице.

Почему в этом случае Apollo не использует кеш?

Вот код, который я использую:

На стороне сервера у меня есть довольно простой запрос для получения статьи из блога, который можно получить по идентификатору или слагу:

type Query {
  ...
  article(id: ID, slug: String): Article
  ...
}

На стороне клиента я запрашиваю статью по слагу, если статья опубликована, или по идентификатору, если это еще черновик.

Запрос по слагу:

<Query
  query = {article}
  variables = {{ slug }}
  fetchPolicy = "cache-and-network"
>
  {({ loading, error, data }) => {
    return (
      <Article
        loading = {loading}
        article = {data && data.article}
      />
    );
  }}
</Query>

Запрос по идентификатору такой же, за исключением параметра переменных, в котором используется идентификатор:

<Query
  query = {article}
  variables = {{ id }}
>
  {({ loading, error, data }) => {
    return (
      <EditArticle loading = {loading} article = {data && data.article} />
    );
  }}
</Query>

Как видите, оба используют одну и ту же конечную точку GraphQL, и результат одинаков. Но кеш не используется.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Что такое Apollo Client и зачем он нужен?
Что такое Apollo Client и зачем он нужен?
Apollo Client - это полнофункциональный клиент GraphQL для JavaScript-приложений, который упрощает получение, управление и обновление данных в...
2
0
3 201
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Apollo предполагает, что ваши резолверы чистые (у них нет побочных эффектов и в основном они возвращают тот же результат при тех же входных данных / аргументах). Это уже многое предположить. Представьте себе преобразователь, который возвращает случайное число или самый новый комментарий на новостном веб-сайте. Оба не всегда возвращали один и тот же результат при одном и том же вводе. С другой стороны, Apollo не делает - и почти не может делать - предположений о реализации вашего распознавателя. Пока в вашей голове реализация распознавателя статей очевидна (если присутствует id, верните статью с этим идентификатором, если есть slug, верните статью с этим слагом), от компьютерной программы сложно догадаться.

Я ответил аналогичный вопрос недавно. Чтобы предотвратить запуск второго запроса, вы должны реализовать перенаправление кеша. Обратной стороной является то, что вы должны синхронизировать перенаправления кеша на клиенте и резолверы на сервере.

Вы правы, я только что понял пару часов назад, что Apollo ничего не знает о моих распознавателях на стороне сервера и не может знать, что запрос вернет тот же результат. Я, вероятно, предположил это, потому что я исхожу из Meteor, где клиент и сервер «связаны» через DDP. Я не знал о перенаправлении кеша (еще не читал документ до этой части), поэтому я использовал функцию client.writeQuery(), чтобы записать результат второго запроса в кеш, когда я получил первый. Я думаю, это очень похоже на перенаправление кеша.

Damien Monni 22.04.2018 15:57

у вас есть пример перенаправления кеша с моими запросами? Вроде хорошо работает с ID, но я не могу понять, как заставить его работать со слагами ...

Damien Monni 22.04.2018 19:13

Это действительно кажется довольно сложным. Я подумал, что будет простой способ получить все элементы с определенным именем типа, и оттуда вы могли бы использовать Array.prototype.find для поиска нужного элемента. Может быть, вы могли бы попытаться спросить их на слабина

Herku 23.04.2018 13:15

Я столкнулся с той же проблемой. По сути, я ожидал, что поиск в кеше просто не удастся, когда он попытается найти только "slug", и меня это устроило, но вместо этого он не смог сгенерировать правильный результат поиска, и результат "null" будет возвращен как ответ на запрос, как если бы это был успешный ответ на запрос. Ой.

Чтобы избежать побочных эффектов, я просто буду использовать отдельный запрос graphQL, который принимает слаг вместо идентификатора. У этого есть несколько других преимуществ, например, я могу использовать поле как «обязательное» в их соответствующих запросах. Главное, что он делает запрос на основе идентификатора более детерминированным и, следовательно, более совместимым с кешированием.

type Query {
  ...
  article(id: ID!): Article
  articleBySlug(slug: String!): Article
  ...
}

Еще лучше была бы возможность поиска в кэше с использованием вашего значения "slug" для получения соответствующего результата, но, похоже, это пока не поддерживается без использования "slug" как части самого идентификатора кеша.

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