【问题标题】:Does Apollo cache the returned data from a mutationApollo 是否缓存突变返回的数据
【发布时间】:2021-03-13 16:53:10
【问题描述】:

我在 React 应用程序中使用 Apollo 客户端,我需要进行突变,然后保留返回的数据以供以后使用(但我无法访问变量),我是否必须使用另一个状态管理解决方案还是我们可以在 Apollo 中做到这一点?

我已经阅读了关于使用查询而不是突变来执行此操作的信息。

这是我目前的代码

// Mutation
const [myMutation, { data, errors, loading }] = useMutation(MY_MUTATION, {
    onCompleted({ myMutation }) {
      console.log('myMutation: ', myMutation.dataToKeep);
      if (myMutation && myMutation.dataToKeep)
        SetResponse(myMutation.dataToKeep);
    },
    onError(error) {
      console.error('error: ', error);
    },
  });

//How I call it

  onClick={() => {
    myMutation({
      variables: {
        input: {
          phoneNumber: '0000000000',
          id: '0000',
        },
      },
    });
  }}

编辑:

这里是突变

export const MY_MUTATION = gql`
  mutation MyMutation($input: MyMutationInput!) {
    myMutation(input: $input) {
      dataToKeep
      expiresAt
    }
  }
`;

以及此突变的架构

MyMutationInput:
  phoneNumber: String!
  id: String!

MyMutationPayload:
  dataToKeep
  expiresAt
  

【问题讨论】:

  • 您使用的是什么版本的 apollo-client?还有你所说的以后使用是什么意思?您的意思是像应用程序中持久存在的全局状态,还是更像是需要在不同组件之间共享的状态?
  • @ManuelPamplona 我正在使用 3.2.9

标签: javascript reactjs graphql react-apollo apollo-client


【解决方案1】:

案例 1:Payload 使用通用实体

简单地说,Apollo 客户端的缓存会保留从查询和突变接收到的所有内容,尽管架构需要包含 id: ID! 字段,并且任何查询都需要使用相关节点上的 id__typename 字段客户端知道要更新缓存的哪一部分。

这假设突变有效负载是模式中的常见数据,可以通过正常查询检索。这是最好的情况。

给定服务器上的以下架构:

type User {
  id: ID!
  phoneNumber: String!
}

type Query {
  user(id: String!): User!
}

type UpdateUserPayload {
  user: User!
}

type Mutation {
  updateUser(id: String!, phoneNumber: String!): UpdateUserPayload!
}

假设cache is used on the client

import { InMemoryCache, ApolloClient } from '@apollo/client';

const client = new ApolloClient({
  // ...other arguments...
  cache: new InMemoryCache(options)
});
  1. 响应中包含的每个可识别对象的缓存 generates a unique ID

  2. 缓存按 ID 将对象存储在平面查找表中。

  3. 只要传入的对象与现有对象使用相同的 ID 存储,这些对象的字段就会合并

    • 如果传入对象和现有对象共享任何字段,则传入对象覆盖这些字段的缓存值。
    • 现有对象或传入对象中出现的字段被保留。

规范化在您的数据图上构建数据图的部分副本 客户端,采用针对读取和更新而优化的格式 当您的应用程序更改状态时生成图表。

客户端的变异应该是

mutation UpdateUserPhone($phoneNumber: String!, $id: String!) {
  updateUser(id: $id, phoneNumber: $phoneNumber) {
    user {
      __typename  # Added by default by the Apollo client
      id          # Required to identify the user in the cache
      phoneNumber # Field that'll be updated in the cache
    }
  }
}

然后,在应用程序中通过同一个 Apollo 客户端使用该用户的任何组件都将自动更新。没什么特别的,客户端默认使用缓存,数据变化时触发渲染。

import { gql, useQuery } from '@apollo/client';

const USER_QUERY = gql`
  query GetUser($id: String!) {
    user(id: $id) {
      __typename
      id
      phoneNumber
    }
  }
`;

const UserComponent = ({ userId }) => {
  const { loading, error, data } = useQuery(USER_QUERY, {
    variables: { id: userId },
  });

  if (loading) return null;
  if (error) return `Error! ${error}`;

  return <div>{data.user.phoneNumber}</div>;
}

fetchPolicy option 默认为 cache-first


案例 2:有效负载是特定于突变的自定义数据

如果数据实际上在架构中的其他地方不可用,则无法如上所述自动使用 Apollo 缓存。

使用其他状态管理解决方案

几个选项:

这是一个使用localStorageexample from the Apollo GraphQL documentation

const [login, { loading, error }] = useMutation(LOGIN_USER, {
  onCompleted({ login }) {
    localStorage.setItem('token', login.token);
    localStorage.setItem('userId', login.id);
  }
});

Define a client-side schema

这是一个纯粹的 Apollo GraphQL 解决方案,因为客户端也是一个状态管理库,它支持有用的开发人员工具并有助于推理数据。

  1. 创建本地架构。

    // schema.js
    export const typeDefs = gql`
      type DataToKeep {
        # anything here
      }
    
      extend type Query {
        dataToKeep: DataToKeep # probably nullable?
      }
    `;
    
  2. 初始化自定义缓存

    // cache.js
    export const dataToKeepVar = makeVar(null);
    
    export const cache = new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            dataToKeep: {
              read() {
                return dataToKeepVar();
              } 
            },
          }
        }
      }
    });
    
  3. 在客户端初始化时应用架构覆盖

    import { InMemoryCache, Reference, makeVar } from '@apollo/client';
    import { cache } from './cache';
    import { typeDefs } from './schema';
    
    const client = new ApolloClient({
      cache,
      typeDefs,
      // other options like, headers, uri, etc.
    });
    
  4. 跟踪突变中的变化:

    const [myMutation, { data, errors, loading }] = useMutation(MY_MUTATION, {
      onCompleted({ myMutation }) {
        if (myMutation && myMutation.dataToKeep)
          dataToKeepVar(myMutation.dataToKeep);
      }
    });
    
  5. 然后,查询@client 字段。

    import { gql, useQuery } from '@apollo/client';
    
    const DATA_QUERY = gql`
      query dataToKeep {
        dataToKeep @client {
          # anything here
        }
      }
    `;
    
    const AnyComponent = ({ userId }) => {
      const { loading, error, data } = useQuery(DATA_QUERY);
    
      if (loading) return null;
      if (error) return `Error! ${error}`;
    
      return <div>{JSON.stringify(data.dataToKeep)}</div>;
    }
    

另请参阅managing local state 上的文档。

【讨论】:

  • "那么,通过应用程序中的同一个 Apollo 客户端使用该用户的任何组件都将自动更新。"我将如何访问缓存中的这个用户?
  • @Mel 没有什么特别需要的,我添加了一个默认使用缓存的简单示例。
  • 当我根据您的示例创建新查询时,我得到“消息”:“字段 'myMutation' 在类型 'Query' 上不存在”,因为 API 中没有此名称的查询.我错过了什么?
  • @Mel 好的,经过一番挖掘,我已经包含了 2 个额外的解决方案:使用另一个状态管理解决方案,就像你建议的那样,以及使用本地状态,因为 Apollo 客户端库也是一个状态管理图书馆。
  • 我最终使用了上下文,但你的答案正是我一开始想做的,谢谢
猜你喜欢
  • 2021-08-08
  • 2020-05-09
  • 1970-01-01
  • 1970-01-01
  • 2018-10-05
  • 2021-06-16
  • 2019-01-24
  • 2018-07-14
  • 2022-11-11
相关资源
最近更新 更多