I'm working on a React project using Tanstack Table to manage the state of the table and Tanstack query to manage the fetching of data from the server.
In my query, I load every 10 emails at a time, as the table scrolls down.
that's my logic:
const lastElementOnTableRef = useRef<HTMLTableRowElement | null>(null);
const lastElementOnCardRef = useRef(null);
As the app is responsive, I have a table ref (for desktops) and a card ref (for mobile), and depending on the screen size I apply a hidden css class to hide them.
Since the problem is only with the table, I'll show it to you and focus only on it.
I use the shadcn table:
<Table>
<TableCaption>Emails enviados</TableCaption>
<TableHeader className="sticky top-0 bg-white">
<TableRow>
<TableHead>Assunto</TableHead>
<TableHead>Destinatários</TableHead>
<TableHead>Data do evento</TableHead>
<TableHead>Enviado</TableHead>
<TableHead>Anexo</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{emails?.map(({ ref, subject, recipients, sentDate, status, hasAttachment, body }, index) => (
<TableRow
ref={index === emails.length - 1 ? lastElementOnTableRef : undefined}
key={ref.id}
Rest of JSX not important...
Note that in the last element of the table, depending on the number of emails loaded, I apply the observer ref to it.
And here's my logic for observing the content and loading more, using the intersection observer API, to check the intersection:
useEffect(() => {
if (!hasNextEmailsList || isFetchingNextEmails) return;
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) fetchNextEmailsList();
},
{ threshold: 1 }
);
const lastElementOnTable = lastElementOnTableRef.current;
const lastElementOnCard = lastElementOnCardRef.current;
if (lastElementOnTable) observer.observe(lastElementOnTable);
if (lastElementOnCard) observer.observe(lastElementOnCard);
return () => {
if (lastElementOnTable) observer.unobserve(lastElementOnTable);
if (lastElementOnCard) observer.unobserve(lastElementOnCard);
};
}, [fetchNextEmailsList, hasNextEmailsList, isFetchingNextEmails, emails]);
When I scroll down the table normally, it works ok, finding the last one, intersecting it and loading 10 more.
However, if I hold down the scroll button at the end of the table, it loads emails every 10 minutes until the list runs out, the scroll should load 10 and adjust itself, but it doesn't if I keep holding it down.
Example: Always holding it at the end:
How to resolve this?
My infinite query:
async function fetchEmailsByContractId({ contractId, pageParam, isUserAdmin }: IEmailsFetchParams) {
const emailsSentRef = collection(firestore, 'emailsSent').withConverter(EmailConverter);
const contractFilter = where('contract', '==', contractId);
const userUidFilter = where('userUid', '==', auth.currentUser?.uid);
const queryFilters = isUserAdmin ? [contractFilter] : [contractFilter, userUidFilter];
const baseQueryConstraints = [...queryFilters, limit(10), orderBy('createdAt', 'desc')];
const queryContraints = pageParam ? [...baseQueryConstraints, startAfter(pageParam)] : baseQueryConstraints;
const filteredDocs = query(emailsSentRef, ...queryContraints);
const sentEmailsSnapshot = await getDocs(filteredDocs);
return sentEmailsSnapshot.docs.map((doc) => ({
ref: doc,
...doc.data(),
}));
}
export function useFetchEmailsQuery(isUserAdmin: boolean) {
const { contractId } = useContractIdStore();
return useInfiniteQuery({
queryKey: [...LIST_EMAILS_QUERY_KEY, contractId],
queryFn: async ({ pageParam }) => fetchEmailsByContractId({ contractId, pageParam, isUserAdmin }),
initialPageParam: undefined as QueryDocumentSnapshot | undefined,
getNextPageParam: (lastPage) => lastPage[lastPage.length - 1]?.ref,
select: (data) => data?.pages.flat(),
enabled: !!contractId,
});
}