从React Apollo客户端写入缓存后,读取缓存的正确方法是什么?

问题描述

我创建了一个基本示例,该示例从graphql服务器读取数据。 我发现执行突变后,网络呼叫会发出请求。 我正在尝试更新缓存以阻止该网络调用,但是当我尝试读取缓存时。

方法可检索数据,但不会自动从缓存中读取:

useQuery

 const { data } = useQuery(GET_PLAYERS);

带有@client指令的useQuery指示获取缓存-失败

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

err =
MissingFieldError {消息:“在ROOT_QUERY对象上找不到字段'玩家'”,路径:Array(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 && 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 && 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 &&
        data.players instanceof Array &&
        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;

任何建议,谢谢

解决方法

首先,@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);

第二,如果要在mutation之后更新缓存,则应使用update选项。像这样:

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