繁体   English   中英

具有自定义合并更新缓存的 ApolloClient fetchMore,但 useQuery 返回旧数据

[英]ApolloClient fetchMore with custom merge updates cache, but useQuery returns old data

我正在尝试根据 ApolloClient 核心分页指南实现分页: https ://www.apollographql.com/docs/react/pagination/core-api

这是我的类型政策:

typePolicies: {
  Query: {
    fields: {
      paginatedProductTracking: {
        // Include everything except 'skip' and 'take' to be able to use `fetchMore`
        //  and repaginate when reading cache
        //  (essential for switching between desktop pagination and mobile lazy loading
        //   without having to refetch)
        keyArgs: (args) => JSON.stringify(omit(args, ['query.skip', 'query.take'])),
        merge: (existing = [], incomingResponse, { args }) => {
          const responseData = incomingResponse?.paginatedData || [];
          return [
            // conservative merge that handles if pages are not requested in order
            ...existing.slice(0, args?.query.skip || 0),
            ...responseData,
            ...existing.slice((args?.query.skip || 0) + responseData.length),
          ];
        },
      },
    },
  },
},

正如您在评论中看到的那样,一个复杂的问题是skiptake在一个名为query的嵌套 arg 中,但它在缓存中看起来很好。

这是我的组件渲染函数(省略了与此问题无关的内容,但如果缺少某些内容,请告诉我:

...
const initialQuery = {
  skip: 0,
  take: 3,
  ...
}

const { data, loading, fetchMore, networkStatus } = useProductTrackingAggregatedDataQuery({
  notifyOnNetworkStatusChange: true,
  variables: {
    query: initalQuery,
});

...

return <InfinityScrollList
  // Apollo merges values for `variables`, but only in a shallow way
  // Hence merging the query params manually
  onLoad={async () => {
    await fetchMore({
      variables: {
        query: {
          ...initialQuery,
          skip: items.length,
        },
      },
    });
  }}
/>

我觉得我做的是对的,因为 Apollo Cache 看起来符合预期,并且在我获取更多条目时它确实会更新:

初始缓存初始缓存

获取后更多在此处输入图像描述

我还可以看到预期的网络请求。

问题是我的组件没有重新渲染:/

我通过将 networkStatus 添加到我的查询结果来强制重新呈现,但我也没有从缓存中获得合并结果(但初始列表)。 这样做,我还注意到我没有收到网络状态 3(fetchMore),但我只看到 1(加载)和 7(待机)。

使用惰性挂钩可能是一种解决方法,但我真的很想避免这种情况,因为我正试图在代码库中设置一个很好的例子,它会忽略缓存失效。

我的数据没有 id 可能是相关的: 在此处输入图像描述

我使用的是最新的 ApolloClient 版本 ( 3.7.1 )。

不幸的是,为此提供一个最小的工作示例将很困难。

好的,我找到了一个解决方案,它是一种有趣的东西,我在官方文档中并没有真正看到它。 我试图只在缓存中写入数组内容 ( paginatedData ) 并认为只要我始终如一地这样做我就很好:

        merge: (existing = [], incomingResponse, { args }) => {
          const responseData = incomingResponse?.paginatedData || [];
          return [
            // conservative merge that handles if pages are not requested in order
            ...existing.slice(0, args?.query.skip || 0),
            ...responseData,
            ...existing.slice((args?.query.skip || 0) + responseData.length),
          ];
        },

我确实在开发工具中看到了自定义合并功能的结果,这听起来像是一个错误。 因为查询钩子仍然返回未合并的数据,它嵌套在一个对象中(我没有在问题中包括这个):

  const items = data?.paginatedProductTracking.paginatedData || [];

所以说这行不通也是有道理的,因为我写在缓存中的数据和API返回的数据不一致。 但是我被表明它正在工作的开发工具绊倒了。

我通过在缓存中写入与 API 响应具有相同类型和结构的数据来解决它:

        typePolicies: {
          Query: {
            fields: {
              paginatedProductTracking: {
                // Include everything except 'skip' and 'take' to be able to use `fetchMore`
                //  and repaginate when reading cache
                //  (essential for switching between desktop pagination and mobile lazy loading
                //   without having to refetch)
                keyArgs: (args) => JSON.stringify(omit(args, ['query.skip', 'query.take'])),
                merge: (existing, incoming, { args }) => {
                  if (!existing) {
                    return incoming;
                  }
                  if (!incoming) {
                    return existing;
                  }
                  const data = existing.paginatedData;
                  const newData = incoming.paginatedData;
                  return {
                    ...existing,
                    // conservative merge that is robust against pages being requested out of order
                    paginatedData: [
                      ...data.slice(0, args?.query.skip || 0),
                      ...newData,
                      ...data.slice((args?.query.skip || 0) + newData.length),
                    ],
                  };
                },
              },
            },
          },
        },

这就是诀窍:)

fetchMore 调用不会更新初始查询的变量,它们所做的只是使用无缓存策略获取更多数据,并通过为给定类型策略实现合并功能将它们写入缓存。 在这里解释

在成功调用 fetchMore 以呈现新数据后,您需要更新初始查询的变量。 此外,将使用 typePolicies 中给定类型的读取函数读取数据,如果未提供,则返回整个缓存结果。 如果返回整个缓存集不是所需的结果,您应该在读取函数中重新实现分页(只有当您的 ui 实现无限滚动之类的东西时才会出现这种情况)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM