Skip to content

robcalcroft/react-use-infinite-loader

Repository files navigation

react-use-infinite-loader ♾️ 📃 ⏳

Netlify Status Run puppeteer tests on the example code npm version

Super lightweight infinite loading hook for React apps

react-use-infinite-loader uses the IntersectionObserver to provide a performant solution to infinite scrolling that doesn't involve scroll event listeners.

⚠️ Some older browsers may not support the IntersectionObserver API, however you can easily polyfill the functionality with this.

As the name suggests react-use-infinite-loader uses React Hooks, so you need to be using React function components to use this library.

Usage

See example/ for a full example (recommended), run it locally with yarn start. Also view it in the browser

Install with

yarn add react-use-infinite-loader

Add to your app

import useInfiniteLoader from 'react-use-infinite-loader';

Implement the hook. Ensure that the initial content page size flows off the page so that the next page isn't instantly fetched

const [canLoadMore, setCanLoadMore] = React.useState(true);
const [data, setData] = React.useState([]);
const loadMore = React.useCallback((page) => {
  loadFromAPI(page).then(response => {
    setCanLoadMore(response.canLoadMore);
    setData(currentData => [...currentData, ...response.data]);
  });
});
const { loaderRef } = useInfiniteLoader({ loadMore, canLoadMore });

Give the loaderRef that's returned from the hook to a div that sits directly below your rendered content list. Give it a classname that you'll use in the next step.

return (
  <>
    <h1>My App</h1>
    {items.map(item => <div>{item}</div>)}
    <div ref={loaderRef} className="loaderRef" />
  </>
);

Add the following CSS to your apps to allow the observer to see the div and know to load more data. Note that if you always have a DOM node with at least 1px height and width below the loaderRef div then you don't need to add this CSS. A common example where the CSS isn't required would be {canLoadMore && <div>Loading page {page + 1}</div>}.

/* You can change the name here and in your JSX if you want to */
.loaderRef {
  width: 1px;
  height: 1px;
  position: absolute;
}

API Reference

useInfiniteLoader arguments object

Property Default value Description
loadMore required Invoked when the user scrolls into the observable viewport + its rootMargin; read about rootMargin and thresholds here.
canLoadMore false Tells useInfiniteLoader whether to run loadMore when the observer is triggered, this is usually set dynamically.
rootMargin "100px 0px 0px 0px" Read about rootMargin here.
threshold 0 Read about threshold here.
initialise true Used for if your data fetching library fetches page 0 and renders it when the component loads, to use this just have a state flag that you set to false once the initial load from your data fetching lib has happened.
startFromPage 0 Used if you already load page 0 on mount, you can tell useInfiniteLoader what page to begin loading more from.
debug false Prints some helpful messages about what useInfiniteLoader is doing.

useInfiniteLoader result object

Property Description
loaderRef A React ref that you must pass to an element via ref={loaderRef}, this element must sit directly below your list of items that you're loading
page The current page that useInfiniteLoader has loaded
resetPage A function that resets the current page to startFromPage (default 0). This is useful if you change the context of the page but the component instance is the same