Optimizing Infinite Scroll Performance

Highlights techniques for optimizing the performance of an infinite scroll feature, including debouncing scroll events and efficiently managing the cache with React Query.
import { useInfiniteQuery } from 'react-query';
import { useEffect, useState } from 'react';

function useInfinitePosts() {
  return useInfiniteQuery(
    async ({ pageParam = 1 }) => {
      const response = await fetch(`/api/posts?page=${pageParam}`);
      return response.json();
      getNextPageParam: (lastPage, pages) => lastPage.nextPage ?? false
This code snippet demonstrates how to fetch data in chunks using React Query's 'useInfiniteQuery' hook. Each chunk corresponds to a 'page' of data from the API. The 'getNextPageParam' returns the value for the next page, or false if there are no more pages.
import { useRef, useCallback } from 'react';
import { useIntersectionObserver } from './useIntersectionObserver';

const InfiniteScrollComponent = ({ data, fetchNextPage }) => {
  const loadMoreRef = useRef();

    target: loadMoreRef,
    onIntersect: fetchNextPage,
    enabled: data?.hasNextPage,

  return (
      { => (
        <React.Fragment key={page.key}>
          { => <div key={}>{item.title}</div>)}
      <div ref={loadMoreRef}>Load More</div>
This code implements the UI component and utilizes an IntersectionObserver hook (`useIntersectionObserver`) to detect when the `Load More` div becomes visible on screen, triggering `fetchNextPage`. The `ref` attached to the `Load More` div moves down with each data fetch, ensuring smooth infinite scrolling.
function debounce(func, wait) {
  let timeout;

  return function executedFunction(...args) {
    const later = () => {

    timeout = setTimeout(later, wait);
This is a debounce function, used to limit the number of times a function (like scrolling events) can be executed over time. This can enhance performance by reducing the number of API calls or heavy computations when scrolling rapidly.