I have a database collection that can be updated by a large number of users in real-time. Users need to be able to see updates from other members, but the collection is large, so re-downloading the entire collection every time is massively inefficient/expensive. Every document has a updateTime timestamp. Given this, what I want to do is poll for updated data as needed (either on component mount or at other frequencies) and merge that into the data already stored in cache with ReactQuery (using persistClientQuery). I'm new to ReactQuery, so I'm wondering if there's a more efficient approach to than the one I'm using here, where I use React Query's useInfiniteQuery hook with the newest updateTime as the nextPageParam: The query itself: 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 }; }Example Usage: 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]);Is all this really necessary, or does ReactQuery support this functionality natively? If not, are there other approaches I should consider here or pitfalls I need to watch out for? (Note: While this code uses Firestore, for various reasons, I don't want to use Firestore's real-time updates here) (责任编辑:) |