简体   繁体   English

使用 ApolloClient 分页 API 导致请求,即使所有页面内容都已在缓存中

[英]Using ApolloClient pagination API results in requests, even if all page content is already in cache

I am using the ApolloClient core pagination API approach to accumulate paginated requests in a merge function and the repaginate them with a read function: https://www.apollographql.com/docs/react/pagination/core-api我正在使用 ApolloClient 核心分页 API 方法在merge function 中累积分页请求,并通过read function 对它们重新分页: https://www.apollographql.com/docs/react/pagination/core-api

This all works, but now there is a request for each page, even the ones that are already in the cache.这一切都有效,但现在每个页面都有一个请求,即使是那些已经在缓存中的页面。

Which defeats the whole purpose when I'm repaginating!当我重新分页时,这破坏了整个目的!

I'm using the default fetchStrategy , cache-first .我使用默认的fetchStrategycache-first

If all requested data is present in the cache, that data is returned.如果缓存中存在所有请求的数据,则返回该数据。 Otherwise, Apollo Client executes the query against your GraphQL server and returns that data after caching it.否则,Apollo Client 将针对您的 GraphQL 服务器执行查询并在缓存后返回该数据。

I wonder how ApolloClient checks that all requested data is in the cache with the pagination implementation.我想知道 ApolloClient 如何使用分页实现检查所有请求的数据是否在缓存中。

Because right now (and the docs seems to rely on this) it always does the request, even when the keyArgs match and the data is in the cache.因为现在(并且文档似乎依赖于此)它总是执行请求,即使keyArgs匹配并且数据在缓存中也是如此。

Does someone know what causes this and how I can customize this cache-first strategy to check if all the items of the requested page are already in the cache?有人知道是什么原因造成的,以及我如何自定义这种cache-first策略来检查请求页面的所有项目是否已经在缓存中?

Here is my code, in case that helps for context or if I'm just doing something wrong:这是我的代码,以防对上下文有帮助或者我只是做错了什么:

        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),
                    ],
                  };
                },
              },
            },
          },
        },
  const [pageSize, setPageSize] = useState(100);
  const [page, setPage] = useState(0);
  const skip = page * pageSize;

  const query = {
    filter,
    aggregationInterval,
    order,
    skip,
    take: pageSize,
    search: search ? values : null,
    locations: currentLocations.length > 0 ? currentLocations.map((location) => location.id) : undefined,
  };

  const { data, loading, fetchMore } = useProductTrackingAggregatedDataQuery({
    variables: {
      query,
    },
  });

      onPageChange={async (newPage) => {
        await fetchMore({
          variables: {
            query: {
              ...query,
              skip: newPage * pageSize,
            },
          },
        });
        setPage(newPage);
      }}

I was recently faced with the exact same issue and had everything implemented in the way the official documentation illustrates until I stumbled upon this issue which is still open so I'm guessing this is still how the fetchMore function actually behaves to date.我最近遇到了完全相同的问题,并且按照 官方文档说明的方式实施了所有内容,直到我偶然发现这个问题仍然存在,所以我猜这仍然是 fetchMore function 迄今为止的实际行为方式。 So @benjamn says that:所以@benjamn 说:

The fetchMore method sends a separate request that always has a fetch policy of no-cache, which is why it doesn't try to read from the cache first. fetchMore 方法发送一个单独的请求,该请求始终具有无缓存的获取策略,这就是它不首先尝试从缓存中读取的原因。

This being the case, fetchMore is only useful if you are implementing an endless scroll sort of pagination where you know beforehand that the new data is not in the cache.在这种情况下,fetchMore 仅在您事先知道新数据不在缓存中的情况下实现无休止的滚动式分页时才有用。

In the pagination documentation it also states that:在分页文档中,它还指出:

If you are not using React and useQuery, the ObservableQuery object returned by client.watchQuery has a method called setVariables that you can call to update the original variables.如果你没有使用 React 和 useQuery,client.watchQuery 返回的 ObservableQuery object 有一个名为 setVariables 的方法,你可以调用它来更新原始变量。

If you change the variables to your query it will trigger your read function implementation.如果您将变量更改为您的查询,它将触发您的读取 function 实现。 And if the read function finds the data within existing it can return them or return undefined which will in turn trigger a.network request to your graphql server to fetch the missing data, which will trigger your merge function to merge the data in the desired way, which will again trigger the read function which will now be able to slice the data you requested according to your { args } out of your existing and return them, which will finally trigger your watched ObservableQuery to fire and your UI to be updated.如果读取 function 在现有数据中找到数据,它可以返回它们或返回未定义,这将触发对您的 graphql 服务器的网络请求以获取丢失的数据,这将触发您的合并 function 以所需方式合并数据,这将再次触发读取 function,它现在能够根据您现有的 {args} 将您请求的数据切片并返回它们,这最终将触发您监视的 ObservableQuery 触发并更新您的 UI。

Now, this approach is counter intuitive and goes against the "recommended" way of implementing pagination, but contrary to the recommended way this approach actually works.现在,这种方法违反直觉并且违背了实现分页的“推荐”方式,但与这种方法实际工作的推荐方式相反。

I was unable to find anything that would prove my conclusions about fetchMore to be wrong, so if any Apollo client guru happens to stumble upon this please do shed some light into this.我无法找到任何可以证明我关于 fetchMore 的结论是错误的东西,所以如果任何 Apollo 客户端大师碰巧发现了这个,请务必阐明这一点。 Until then the only solution I can offer is working with setVariables instead of fetchMore.在那之前,我能提供的唯一解决方案是使用 setVariables 而不是 fetchMore。

Keep in mind that you will need to implement a read function along with your merge.请记住,您需要在合并时实施读取 function。 It will be responsible for slicing your cached data and triggering a.network request by returning undefined if it was unable to find a full slice.它将负责切片您的缓存数据并在无法找到完整切片时通过返回 undefined 来触发网络请求。

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

相关问题 如何最好地使用 ApolloClient / InMemoryCache 并为 API 启用按需缓存? - How to best use ApolloClient / InMemoryCache and enable Cache on demand for API's? 删除所有选定的行,即使我在其他页面中使用带有受控分页的反应表 - Remove all selected rows even though I'm in other page using react-table with controlled pagination 如何使用 readQuery/writeQuery 为 ApolloClient 缓存更新编写单元测试? - How to write unit test for ApolloClient cache update using readQuery/writeQuery? Javascript/React 中 API 请求的分页? - Pagination on API requests in Javascript/React? 新的ApolloClient()导致WebPack错误? - new ApolloClient() results in WebPack error? 使用React使用Chrome扩展程序更改现有页面上的内容 - Using React to change content on already present page using chrome extension 检查现有的 ApolloClient 实例以清除缓存? - Check for existing instance of ApolloClient to clear cache? 带有嵌套查询结果的 ApolloClient v3 fetchMore - ApolloClient v3 fetchMore with nested query results 将所有 /api/.. 请求重定向到 express - redirect all /api/.. requests to express 具有自定义合并更新缓存的 ApolloClient fetchMore,但 useQuery 返回旧数据 - ApolloClient fetchMore with custom merge updates cache, but useQuery returns old data
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM