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

时间:2020-08-26 19:39:04

标签: reactjs typescript graphql apollo-client

我创建了一个基本示例,该示例从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;

任何建议,谢谢

1 个答案:

答案 0 :(得分:0)

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