Introducing useInfiniteScroll: a simple React hook for infinite scroll experience
Project link: https://github.com/closeio/use-infinite-scroll
We’re excited to introduce the newest addition to our frontend-related open
source projects. useInfiniteScroll is a super simple React hook for creating
an infinite scroll experience based on the IntersectionObserver API.
We are taking advantage of this tiny library in our Pipeline View feature (Trello-like board), where there are multiple columns, each scrollable separately. In the Pipeline View, we also use another library that we open sourced called react-custom-scroller, which allows use to have cross-browser and cross-device scrollbars.
Summary
- Extremely lightweight (less than 1KB minzipped).
- It uses the
IntersectionObserverAPI, so it doesn't need to listen toscrollevents, which are known to cause performance issues. - No other 3rd-party dependencies.
Installation
yarn add @closeio/use-infinite-scrollAPI
The API of useInfiniteScroll hook is pretty straightforward.
It takes an options object:
type UseInfiniteScrollOptions = {
// The observer will disconnect when there are no more items to load.
hasMore: boolean;
// Pass true when you're re-fetching the list and want to resets the scroller
// to page 0.
// Defaults to `false`.
reset?: string;
// When scrolling, the distance in pixels from the bottom to switch the page.
// Defaults to `250` (in px).
distance?: number;
};And it returns a tuple:
type UseInfiniteScrollResult = [
number, // The current page (starting at 0)
RefObject<T>, // React ref to a loader (spinner) element
RefObject<T>, // React ref to the scroll container element
];Example
import React from 'react';
import useInfiniteScroll from '@closeio/use-infinite-scroll';
export default function MyComponent() {
const [items, setItems] = useState([]);
const [hasMore, setHasMore] = useState(false);
const [page, loaderRef, scrollerRef] = useInfiniteScroll({ hasMore });
useEffect(async () => {
const data = await myApiCall({ page });
setHasMore(data.hasMore);
setItems((prev) => [...prev, data.items]);
}, [page]);
return (
<div ref={scrollerRef}>
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
{hasMore && <div ref={loaderRef}>Loading…</div>}
</div>
);
}What it looks like
