Как правильно читать кеш после записи в кеш из react apollo-client?

#reactjs #typescript #graphql #apollo-client

#reactjs #typescript #graphql #apollo-client

Вопрос:

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

Этот метод работает для извлечения данных, но он не выполняет автоматическое чтение из кэша:

Запрос использования

  const { data } = useQuery(GET_PLAYERS);
  

Используйте запрос с директивой @client для указания get cache — сбой

 const GET_PLAYERS = gql`
  query getPlayers {
    players @client {
      __typename
      _id
      name
      score
    }
  }
`;
 const { data } = useQuery(GET_PLAYERS);
  

ошибка =
MissingFieldError {сообщение: «Не удается найти поле ‘players’ в объекте ROOT_QUERY», путь: Массив (1), запрос: {…}, переменные: {…}}
сообщение: «Не удается найти поле «игроки» в объекте ROOT_QUERY»

использование client.readQuery() ничего не возвращает как при загрузке, так и после мутации, по-прежнему безрезультатно:

   const obj = client.readQuery({ query: GET_PLAYERS });
  

Вот мой полный код.

 import React, { FC } from 'react';
import 'cross-fetch/polyfill'; // patch for tests: Error: fetch is not found globally and no fetcher passed, to fix pass a fetch for your environment
import {
  gql,
  useQuery,
  useMutation,
  ApolloClient,
  InMemoryCache,
  ApolloProvider
} from '@apollo/client';

const uri = 'http://localhost:4000/graphql';

const client = new ApolloClient({
  uri,
  cache: new InMemoryCache()
});

const ADD_PLAYER = gql`
  mutation AddPlayer($name: String!) {
    addPlayer(player: { name: $name }) {
      _id
      name
      score
    }
  }
`;

// example with directive = @client to try to get cached version
// const GET_PLAYERS = gql`
//   query getPlayers {
//     players @client {
//       __typename
//       _id
//       name
//       score
//     }
//   }
// `;

const GET_PLAYERS = gql`
  query getPlayers {
    players {
      _id
      name
      score
    }
  }
`;

interface IData {
  data: {
    players: Array<{ name: string }>;
  };
  loading: boolean;
}

interface IPlayer {
  name: string | null;
}

const App: FC = () => {
  const { data } = useQuery(GET_PLAYERS);
  // const { data } = client.readQuery({ query: GET_PLAYERS });
  // console.log('obj = ', obj);
  // const data = obj amp;amp; obj.data;

  const [addPlayer] = useMutation(ADD_PLAYER, {
    update: (cache, { data: mutationData }) => {
      try {
        const cacheGetPlayers: { players: Array<IPlayer> } | null = cache.readQuery({
          query: GET_PLAYERS
        });

        const cachePlayers: Array<IPlayer> = (cacheGetPlayers amp;amp; cacheGetPlayers.players) || [
          { name: null }
        ];

        let playersUpdated: Array<IPlayer> = { ...cachePlayers };

        const player = mutationData.addPlayer;

        if (player) {
          // ie not already existing !!!

          if (playersUpdated) {
            if (!cachePlayers.find((item: IPlayer) => player.name !== item.name)) {
              playersUpdated.push(player);
            }
          } else {
            playersUpdated = [player];
          }
          cache.writeQuery({
            query: GET_PLAYERS,
            data: {
              getPlayers: {
                players: playersUpdated
              }
            }
          });
        }
      } catch (err) {
        console.log('err = ', err);
      }
    }
  });

  const handleClick = () => {
    const name = 'mary';

    addPlayer({
      variables: { name },
      // example data:
      // optimisticResponse: {
      //   __typename: 'Mutation',
      //   addPlayer: {
      //     _id: ObjectId('4edd40c86762e0fb12000003'), // 4edd40c86762e0fb12000003
      //     score: 0,
      //     name
      //   }
      // },
      refetchQueries: [{ query: GET_PLAYERS }]
    });
  };

  return (
    <div>
      list of existing names = {data amp;amp;
        data.players instanceof Array amp;amp;
        data.players.map((item: { name: string }) => `,${item.name}`)}

      <button type="button" onClick={handleClick}>
        Click to add player
      </button>
    </div>
  );
};

const Prov = () => (
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>
);

export default Prov;
  

Любой совет, спасибо

Ответ №1:

Во-первых, директива @client просто использует для запроса данных, которые вы должны инициировать в players поле в кэше с помощью __typename . Вот так:

 const cache = new InMemoryCache()

const client = new ApolloClient({
    cache,
    link,
})

cache.writeData({
    data: {
    players: {
      items: [],
      __typename: Player
    }
  },
})

...

const GET_PLAYERS = gql`
  query {
    players @client {
      __typename
      items {
        _id
        name
        score
      }
    }
  }
`;
 const { data } = useQuery(GET_PLAYERS);  

Во-вторых, если вы хотите обновить кеш после мутации, вам следует использовать update опцию. Вот так:

 useMutation(UPDATE_PLAYER, {
      update(cache, { data: { player } }) {
        const { players } = cache.readQuery(options)
        cache.writeQuery({
          ...options,
          data: {
            players: { ...players, items: [player, ...players.items] },
          },
        })
      },
    }
  }
)  

Комментарии:

1. Спасибо за предложение. Просто смотрю это. Кажется, есть несколько способов сделать что-то, и на сайте Apollo не хватает полных примеров. Ваш пример, похоже, относится к — apollo-link-state, который является другим способом записи данных. Я изучаю это, но я все равно хотел бы знать, как записывать кеш обоими способами, т. Е. Используя @apollo / client include.