Skip to main content
@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

NameTypeRequired?DefaultDescription
bottomTriggerElementReact.RefObjectyes-Holds a ref to an element that acts as sentinel for scroll intersection
disabledbooleannofalseIf set to true, the hook will stop detecting scroll

Returns

Returns boolean that will be true when the respective target element is scrolling.