【问题标题】:Correct way to remove item from client side cache in apollo-client从 apollo-client 中的客户端缓存中删除项目的正确方法
【发布时间】:2020-11-19 19:45:49
【问题描述】:

我在带有内存缓存的 React(Typescript) 应用程序中使用 GraphQL 和 Apollo-Client。缓存会根据添加的新项目进行更新,这可以正常工作且没有错误。

当删除项目时,GraphQL Apollo-Server 后端会返回一个字符串,说明删除操作成功,该操作会启动要调用的更新函数,该函数读取缓存,然后通过过滤掉项目的 id 来修改它。这是使用来自 Apollo-Client 的突变钩子执行的。

const [deleteBook] = useMutation<{ deleteBook: string }, DeleteBookProps>(DELETE_BOOK_MUTATION, {
    variables: { id },
    onError(error) {
      console.log(error);
    },
    update(proxy) {
      const bookCache = proxy.readQuery<{ getBooks: IBook[] }>({ query: GET_BOOKS_QUERY });
      if (bookCache) {
        proxy.writeQuery<IGetBooks>({
          query: GET_BOOKS_QUERY,
          data: { getBooks: bookCache.getBooks.filter((b) => b._id !== id) },
        });
      }
    },
  });

该功能有效并且前端使用缓存中的正确项目更新,但是控制台中显示以下错误:


Cache data may be lost when replacing the getBooks field of a Query object.

To address this problem (which is not a bug in Apollo Client), define a custom merge function for the Query.getBooks field, so InMemoryCache can safely merge these objects:

  existing: [{"__ref":"Book:5f21280332de1d304485ae80"},{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]
  incoming: [{"__ref":"Book:5f212a1332de1d304485ae81"},{"__ref":"Book:5f212a6732de1d304485ae82"},{"__ref":"Book:5f212a9232de1d304485ae83"},{"__ref":"Book:5f21364832de1d304485ae84"},{"__ref":"Book:5f214e1932de1d304485ae85"},{"__ref":"Book:5f21595a32de1d304485ae88"},{"__ref":"Book:5f2166601f6a633ae482bae4"}]

For more information about these options, please refer to the documentation:

  * Ensuring entity objects have IDs: https://go.apollo.dev/c/generating-unique-identifiers
  * Defining custom merge functions: https://go.apollo.dev/c/merging-non-normalized-objects

有没有更好的方法来更新缓存以免收到此错误?

【问题讨论】:

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


    【解决方案1】:

    我也遇到了完全相同的警告,不幸的是,除了这里建议的解决方案之外,我没有找到其他解决方案:https://go.apollo.dev/c/merging-non-normalized-objects

    const client = new ApolloClient({
      ....
      cache: new InMemoryCache({
        typePolicies: {
          Query: {
            fields: {
              getBooks: {
                merge(existing, incoming) {
                  return incoming;
                },
              },
            },
          },
        }
      }),
    });
    

    (我不确定我是否正确地编写了您的字段和类型,因此您可以稍微更改此代码)

    基本上,上面的代码让我们来看看 apollo 客户端如何处理可合并的数据。在这种情况下,我只需将旧数据替换为新数据即可。

    我想知道是否有更好的解决方案

    【讨论】:

    • 谢谢,解决了错误信息。看起来很奇怪,我猜 Apollo-Client 只是想要一个明确的方法来合并查询。
    【解决方案2】:

    我也遇到了同样的问题。我遇到了一个 GitHub 线程,它提供了两种替代解决方案 here

    首先是在调用cache.writeQuery之前清除缓存中的内容:

      cache.evict({
        // Often cache.evict will take an options.id property, but that's not necessary
        // when evicting from the ROOT_QUERY object, as we're doing here.
        fieldName: "notifications",
        // No need to trigger a broadcast here, since writeQuery will take care of that.
        broadcast: false,
      });
    

    简而言之,这会刷新您的缓存,因此您的新数据将成为新的事实来源。无需担心丢失旧数据。

    apollo-client v3 的替代建议在下面的same thread 中进一步发布:

    cache.modify({
      fields: {
        notifications(list, { readField }) {
          return list.filter((n) => readField('id', n) !==id)
        },
      },
    })
    

    这种方式删除了大量样板,因此您无需使用readQueryevictwriteQuery。问题是,如果你在运行 Typescript,你会遇到一些实现问题。在底层使用的格式是InMemoryCache 格式,而不是通常的 GraphQL 数据。您将看到 Reference 对象、无法推断的类型以及其他奇怪的东西。

    【讨论】:

    • 无法让 cache.evict 工作,但无论如何 cache.modify 似乎是最好的方法
    猜你喜欢
    • 2020-11-21
    • 2020-11-27
    • 2021-06-05
    • 2017-03-18
    • 1970-01-01
    • 2020-05-15
    • 2021-01-24
    • 2023-03-13
    • 2020-08-07
    相关资源
    最近更新 更多