A simple React Hook for infinite scrolling built on the Intersection Observer API
npm install use-simple-infinite-scroll









``sh`
npm install use-simple-infinite-scroll

`tsx
import React, { useState } from 'react';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';
type Item = {
id: number;
name: string;
};
type Result = {
data: Item[];
nextCursor: number | null;
};
const canFetchMore = (nextCursor: Result['nextCursor']) => nextCursor !== null;
const InfiniteScrollExample = () => {
const [isLoading, setIsLoading] = useState(false);
const [items, setItems] = useState
const [nextCursor, setNextCursor] = useState
const fetchMore = () => {
setIsLoading(true);
fetch(/api/items?cursor=${nextCursor})
.then((res) => res.json())
.then((res: Result) => {
setItems([...items, ...res.data]);
setNextCursor(res.nextCursor);
setIsLoading(false);
});
};
const [targetRef] = useSimpleInfiniteScroll({
onLoadMore: fetchMore,
canLoadMore: canFetchMore(nextCursor),
});
return (
<>
{items.length !== 0 ? (
$3
`tsx
import React from 'react';
import { useInfiniteQuery } from 'react-query';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';type Item = {
id: number;
name: string;
};
type Result = {
data: Item[];
nextCursor: number | null;
};
const InfiniteScrollExample = () => {
const {
status,
data,
error,
isFetching,
isFetchingMore,
fetchMore,
canFetchMore,
} = useInfiniteQuery(
'items',
(key: string, cursor = 0) =>
fetch(
/api/items?cursor=${cursor}).then((res) => res.json()),
{
getFetchMore: (lastGroup) => lastGroup.nextCursor,
},
); const [targetRef, rootRef] = useSimpleInfiniteScroll({
onLoadMore: fetchMore,
canLoadMore: !!canFetchMore,
});
return status === 'loading' ? (
Loading...
) : status === 'error' ? (
Error: {error && error.message}
) : (
style={{
overflow: 'auto',
}}
ref={rootRef}
>
{data &&
data.map((page, i) => (
{page.data.map((item) => (
- {item.name}
))}
))}
type="button"
ref={targetRef}
onClick={() => fetchMore()}
disabled={!canFetchMore || !!isFetchingMore}
>
{isFetchingMore
? 'Loading more...'
: canFetchMore
? 'Load More'
: 'Nothing more to load'}
{isFetching && !isFetchingMore ? 'Background Updating...' : null}
API
`ts
const useSimpleInfiniteScroll: (options: {
canLoadMore: boolean;
onLoadMore: () => void;
rootMargin?: string;
threshold?: number | number[];
}) => [(target: Element | null) => void, (root: Element | null) => void];
`| Name | Type | Default | Required | Descripttion |
| :------------ | :------------------- | :------ | :------: | :----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
canLoadMore | boolean | | ✓ | Specifies if there are more entities to load. |
| onLoadMore | () => void | | ✓ | Called when the user has scrolled all the way to the end. |
| rootMargin | string | "0px" | | Margin around the root. Can have values similar to the CSS margin property, e.g. "10px 20px 30px 40px" (top, right, bottom, left). |
| threshold | number \| number[] | 0 | | Either a single number or an array of numbers which indicate at what percentage of the target's visibility the observer's callback should be executed. |For more information on
rootMargin and threshold option, visit the MDN web docs.FAQ
$3
You can wrap multiple
ref assignments in a single useCallback:`tsx
import React, { useRef, useCallback } from 'react';
import { useSimpleInfiniteScroll } from 'use-simple-infinite-scroll';const AssignMultipleRefsExample = () => {
const rootRef = useRef();
const targetRef = useRef();
const [setTargetRef, setRootRef] = useSimpleInfiniteScroll({
onLoadMore: () => {},
canLoadMore: true,
});
// Use
useCallback so we don't recreate the function on each render - Otherwise, the function passed to onLoadMore option will be called twice
const setRootRefs = useCallback(
(node: HTMLDivElement | null) => {
// Ref's from useRef needs to have the node assigned to current
rootRef.current = node;
// Callback refs, like the one from useSimpleInfiniteScroll, is a function that takes the node as an argument
setRootRef(node);
},
[setRootRef],
); const setTargetRefs = useCallback(
(node: HTMLDivElement | null) => {
targetRef.current = node;
setTargetRef(node);
},
[setTargetRef],
);
return (
);
};
`$3
use-simple-infinite-scroll supports all of the major modern browsers.
Browsers like IE11 are not supported: if you need to support older browsers you can add IntersectionObserver polyfill.
You can install the polyfill via npm or by downloading a zip of this repository:
`sh
npm install intersection-observer
`Then import it in your app:
`js
import 'intersection-observer';
``Contributions are always welcome! Please read the contributing first.
Thanks goes to these wonderful people (emoji key):
Kotaro Sugawara 💻 📖 🤔 🚇 ⚠️ |
This project follows the all-contributors specification. Contributions of any kind welcome!