我有一个数据库集合,可以由大量用户实时更新。用户需要能够看到其他成员的更新,但集合很大,所以每次重新下载整个集合是非常低效/昂贵的。
每个文档都有一个updateTime
时间戳。考虑到这一点,我想做的是根据需要轮询更新的数据(无论是在组件挂载时还是以其他频率),并使用ReactQuery(使用persistClientQuery
)将其合并到已经存储在缓存中的数据中。
我是ReactQuery的新手,所以我想知道是否有比我在这里使用的方法更有效的方法,我使用React Query的useInfiniteQuery
钩子,最新的updateTime
作为nextPageParam
:
查询本身:
export function useTalentWithStore() {
const queryClient = useQueryClient();
const query = useInfiniteQuery<Talent[]>({
queryKey: ['talent'],
getNextPageParam: (lastPage, allPages) => {
const data = allPages.flat();
// Use the newest document's updateTime as the next page param
const times = data.map((tal) => tal?.docMeta?.updateTime);
if (data.length) {
const max = Math.max(...times as any);
return new Date(max);
}
return undefined;
},
queryFn: async ({ pageParam }) => {
let talentQuery: firebase.firestore.CollectionReference<firebase.firestore.DocumentData> | firebase.firestore.Query<firebase.firestore.DocumentData>
= firestore.collection("talent");
// If the there's a page param, just get documents updated since then, otherwise, get everything
if (pageParam) {
talentQuery = talentQuery.where("docMeta.updateTime", ">", pageParam);
}
let talentSnapshot = await talentQuery.get();
const talentUpdates: Talent[] = talentSnapshot.docs.map((doc) => {
return {
id: doc.id,
...doc.data()
}
});
return talentUpdates;
},
staleTime: Infinity,
});
// Combine new data with any old data, and return a flat object
const flatData = useMemo<Talent[] | undefined>(() => {
const oldData = query.data?.pages?.[0] || [];
const newData = query.data?.pages?.[1] || [];
const combinedData: Talent[] = [];
if (oldData) {
combinedData.push(...oldData);
}
for (const tal of newData) {
const idx: number = combinedData.findIndex((t) => t.id === tal.id);
if (idx >= 0) {
combinedData[idx] = tal;
} else {
combinedData.push(tal);
}
}
// If there's any old data, flush it out and replace it with the combined new data
if (oldData.length) {
queryClient.setQueryData(['talent'], (data: any) => ({
pages: [combinedData],
pageParams: query.data?.pageParams,
}));
}
return combinedData;
}, [query.data, queryClient]);
return { ...query, flatData };
}
字符串
用法示例:
const talentQuery = useTalentWithStore();
const talent = talentQuery.flatData;
const [fetchedOnMount, setFetchedOnMount] = useState(false);
useEffect(() => {
if (!fetchedOnMount && !talentQuery.isFetching) {
console.log(`Fetching New Talent`, !fetchedOnMount && !talentQuery.isFetching);
talentQuery.fetchNextPage();
}
setFetchedOnMount(true);
}, [talentQuery, fetchedOnMount]);
型
这一切真的有必要吗?或者ReactQuery本身就支持这个功能吗?
如果没有,我是否应该考虑其他方法或需要注意的陷阱?
(Note:虽然这段代码使用了Firestore,但由于各种原因,我不想在这里使用Firestore的实时更新)
1条答案
按热度按时间zkure5ic1#
把这个献给其他沿着蹒跚而行的人!
有一个更简单的解决方案,基本上是使用普通的
query
并将queryClient
传递到queryFn
中,这样您就可以使用它来获取任何旧数据并使用最近记录的日期/时间过滤查询。如果您将staleTime设置为
Infinity
(如前所述),并将refetchOnMount
设置为always
,则将获得一个查询,该查询保存以前的结果,每当查询重新装载时获取任何更新,并使用您指定的任何合并函数合并这些更新。字符串