@spa-tools/interaction-hooks
useInfiniteScroll()
Stop time-warping your users back to the 1990's with antiquated pagination controls. Instead, streamline your data pagination experience by implementing infinite scrolling with the useInfiniteScroll hook.
Usage
import { useEffect, useRef } from 'react';
import { useCallEndpoint } from '@spa-tools/api-client';
import { useInfiniteScroll } from '@spa-tools/interaction-hooks';
// here we create a custom hook to fetch recipes from a server
// using the useCallEndpoint hook from the @spa-tools/api-client package
function useGetRecipes() {
  return useCallEndpoint(
    'https://dummyjson.com/recipes',
    {
      requestOptions: { recordLimit: 10 },
      serverModelOptions: { jsonDataDotPath: 'recipes' },
    },
    // we pass true to enable appending of new records
    true
  );
}
function UseInfiniteScrollExample() {
  // this will hold the ref to our scroll target, which is just a div that we
  // place below our list of recipes to act as a sentinel for scroll intersection
  const scrollTargetRef = useRef<HTMLDivElement>(null);
  const [getRecipes, recipesResult, isRecipesCallPending] = useGetRecipes();
  // anytime our scroll target is intersected for vertical scroll, the hook
  // will return true, which is how we know to fetch the next page of recipes
  const isScrolling = useInfiniteScroll(scrollTargetRef);
  const count = recipesResult?.data?.length ?? -1;
  const total = recipesResult?.total ?? 0;
  useEffect(() => {
    if ((isScrolling && !isRecipesCallPending && count < total) || (!isRecipesCallPending && !recipesResult)) {
      getRecipes();
    }
  }, [count, getRecipes, isRecipesCallPending, isScrolling, recipesResult, total]);
  return (
    <div>
      <h4>
        {count && total ? `${count === total ? `All ${count}` : `${count} of ${total}`} recipes retrieved!` : ''}
        {count && total && count < total ? ' (scroll recipe list to load more)' : ''}
      </h4>
      <div style={{ height: '300px', overflowY: 'auto', padding: '1rem', width: '100%' }}>
        <ul>{recipesResult?.data?.map((recipe) => <li key={recipe.id}>{recipe.name}</li>)}</ul>
        {isRecipesCallPending && <div>Loading recipes...</div>}
        <div ref={scrollTargetRef} />
      </div>
    </div>
  );
}
Parameters
| Name | Type | Required? | Default | Description | 
|---|---|---|---|---|
bottomTriggerElement | React.RefObject | yes | - | Holds a ref to an element that acts as sentinel for scroll intersection | 
disabled | boolean | no | false | If set to true, the hook will stop detecting scroll | 
Returns
Returns boolean that will be true when the respective target element is scrolling.