fetchMore
函数,结合 updateQuery
来告诉 Apollo 如何使用结果更新其缓存(将其附加到你已经拥有的)。因此轮询会重新获取您的初始查询并覆盖缓存,因此您会丢失分页以及通过
fetchMore
添加到缓存中的所有 updateQuery
内容。
我的问题:如何同时进行轮询和分页,同时保持良好的用户体验? (无需订阅)
这是我手动实现轮询的方法。它在
onCompleted
中开始轮询,并在 handleFetchMore
中重置。
如果发出带有更改的选项/变量的重新获取查询,则它看起来像是 Apollo 客户端的新查询,因此在加载重新获取查询期间数据将设置为未定义。为了避免轮询重新获取时出现空数据,使用缓冲数据状态。
export const MyComponent = () => {
// if a refetch-query is issued with changed options / variables, it looks like a new query for Apollo Client and hence the data is set to undefined during loading. To avoid empty data on polling-refetch, a buffered data state is used
const [bufferedData, setBufferedData] = useState<
YourDataType | undefined
>();
const intervalId = useRef<NodeJS.Timeout | undefined>();
const resetPolling = () => {
clearInterval(intervalId.current);
intervalId.current = undefined;
};
const POLL_INTERVAL = 5000;
const { fetchMore, refetch, variables, networkStatus } =
useQuery({
variables: {
options: {
filter,
limit: itemsPerPage,
offset: 0,
},
},
onCompleted: (data: YourDataType) => {
if (data) {
setBufferedData(data);
// start polling
const newLastOffset = data?.items.length ?? 0;
if (!intervalId.current) {
intervalId.current = setInterval(() => {
refetch({
...variables,
options: {
...variables?.options,
offset: 0,
limit: newLastOffset,
},
});
}, POLL_INTERVAL);
}
}
},
onError: () => setBufferedData(undefined),
});
const fetchingMore = networkStatus === NetworkStatus.fetchMore;
const handleFetchMore = useCallback(() => {
if (!fetchingMore) {
resetPolling();
fetchMore({
variables: {
options: {
filter,
limit: itemsPerPage,
offset: bufferedData?.items.length ?? 0,
},
},
updateQuery: (prev, { fetchMoreResult }) => {
if (!fetchMoreResult || !fetchMoreResult) return prev;
const merged = [...(prev?.items ?? [])];
(fetchMoreResult.items ?? []).forEach((item) => {
if (
item &&
!merged.find(
(mergedItem) => mergedItem && mergedItem.id === item.id
)
) {
merged.push(item);
}
});
return merged;
},
});
}
}, [bufferedData?.items.length, fetchMore, fetchingMore]);
};