![](/img/trans.png)
[英]When using fetchMore with useQuery previous data is undefined
[英]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),
];
},
},
},
},
},
正如您在评论中看到的那样,一个复杂的问题是skip
和take
在一个名为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(待机)。
使用惰性挂钩可能是一种解决方法,但我真的很想避免这种情况,因为我正试图在代码库中设置一个很好的例子,它会忽略缓存失效。
我使用的是最新的 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.