Есть ли способ определить, когда данные, используемые запросом, извлекаются из кэша клиента Apollo? - PullRequest
9 голосов
/ 21 февраля 2020

Извиняюсь за длинный вопрос, но я был бы очень признателен за некоторые мысли / помощь по лучшей стратегии для аннулирования кэша и повторного запроса запросов в Apollo Client 3.

Фон

Первый Немного информации о сценарии, который я себе представляю:

  • Существует компонент Account (пример ниже), который использует хук useQuery из Reaction-Apollo для получения и отображения некоторых баз c информация об учетной записи и список транзакций для этой учетной записи
  • В другом месте приложения есть компонент CreateTransactionForm, который использует мутацию для вставки новой транзакции. Это отдельный компонент, который находится в другом месте в дереве компонентов и не обязательно является дочерним по отношению к AccountComponent)
  • . Важно отметить, что процесс хранения транзакции на сервере имеет некоторую нетривиальную сторону эффекты помимо вставки фактической транзакции в базу данных:
    • все остальные транзакции, которые происходят после вставляемая (в хронологическом порядке), обновляются с новыми текущими балансами
    • любой связанный счет обновлены с новым текущим балансом

Упрощенная c версия моего Account компонента может выглядеть примерно так:

import { gql, useQuery } from '@apollo/client';
import React from 'react';
import { useParams } from 'react-router-dom';

const GET_ACCOUNT_WITH_TRANSACTIONS = gql`
  query getAccountWithTransactions($accountId: ID!) {
    account(accountId: $accountId) {
      _id
      name
      description
      currentBalance
      transactions {
        _id
        date
        amount
        runningBalance
      }
    }
  }
`;

export const Account: React.FunctionComponent = () => {
  const { accountId } = useParams();
  const { loading, error, data } = useQuery(GET_ACCOUNT_WITH_TRANSACTIONS, {
    variables: { accountId },
  });

  if (loading) { return <p>Loading...</p>; }
  if (error) { return <p>Error</p>; }

  return (
    <div>
      <h1>{data.account.name}</h1>

      {data.account.transactions.map(transaction => (
        <TransactionRow key={transaction._id} transaction={transaction} />
      ))}
    </div>
  );
};

Потенциальные стратегии

Я оцениваю различные варианты аннулирования частей кэша клиента Apollo и повторного получения соответствующих данных после вставки транзакции. Из того, что я узнал, есть несколько потенциальных стратегий:

a) вызвать метод refetch, возвращаемый useQuery, чтобы заставить Account компонент перезагрузить свои данные

  • это кажется надежным и будет go возвращаться на сервер для данных fre sh, но CreateTransactionForm должен быть (прямо или косвенно) связан с компонентом Account, потому что что-то должно сработать при вызове refetch

b) передать имя запроса (getAccountWithTransactions) в параметр refetchQueries мутации

  • , аналогичный a, но с потенциально даже более тесная связь - CreateTransactionForm должен был бы знать обо всех других компонентах / запросах, которые существуют в приложении и могут быть затронуты мутацией (и если в будущем будет добавлено еще больше, это будет означать, что нужно обновить CreateTransactionForm)

c) вручную изменить содержимое кэша после выполнения мутаций

  • Я думаю, что это будет довольно сложно / трудно поддерживать, потому что CreateTransactionForm будет Чтобы узнать, какие именно данные изменились в результате действий сервера. Как уже упоминалось, это может быть не тривиальный объем данных, и после выполнения мутации нам потребуется извлечь обновленные данные не только о транзакции, которая была вставлена, но также и о любых других, которые были обновлены как побочный эффект, плюс затронутые учетные записи, и др c. Это также может быть не очень эффективно, потому что часть этой информации никогда не будет снова отображаться на клиенте.

Моя интуиция заключается в том, что ни один из описанных выше вариантов не кажется идеальным. В частности, меня беспокоит возможность сопровождения по мере роста приложения; если компоненты должны иметь четкие знания о том, какие именно компоненты / запросы могут быть затронуты изменениями, внесенными в граф данных, то возникает ощущение, что будет очень легко пропустить один и внести незначительные ошибки, когда приложение станет больше и больше complex.

Лучший способ?

Я очень заинтересован в новых методах evict и gc, представленных в Apollo Client 3, и мне интересно, могут ли они обеспечить более точное решение.

Что я думаю, так это то, что после вызова мутации я мог бы использовать эти новые возможности для:

  • агрессивного удаления массива transactions на всех учетных записях, которые включены в транзакцию
  • также, исключите поле currentBalance на любых затронутых учетных записях

, например:

  const { cache } = useApolloClient();
  ...
  // after calling the mutation:
  cache.evict(`Account:${accountId}`, 'transactions');
  cache.evict(`Account:${accountId}`, 'currentBalance');
  cache.gc();

Вышеприведенное обеспечивает простой способ удаления устаревших данных из кэша и гарантирует, что компоненты будут go подключаться к сети при следующем выполнении запросов этих полей. Это хорошо работает, если я перейду к другой странице и вернусь, например, к странице Account.

Мой главный вопрос (наконец-то!)

Это приводит к основной части головоломка, в которой я не уверен:

есть ли способ обнаружить, что некоторые или все данные, на которые есть ссылки в запросе, были удалены из кэша?

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

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

...