Skip to content

useInfiniteScroll

Hook to deal with large sets of data. It allow users to scroll through content endlessly without explicit pagination or loading new pages.

Demo

INFO

The component uses useInfiniteScroll hook to render an items list that load all items while user scroll its container.

Loading demo…
Show source code
tsx
import { useCallback, useMemo, useRef } from "react";
import { useInfiniteScroll } from "../../../..";

export const UseInfiniteScroll = () => {
	const ref = useRef<HTMLDivElement>(null);
	const resultData = useMemo(() => Array(40).fill(undefined).map((_, index)=> index.toString()),[]);

	const getLoadMoreList = useCallback((data?: string[]): Promise<string[]> => {
		let list;
		if (!data) {
			list = resultData.slice(0, 10);
		} else {
			const limit = 10;
			let start = 0;
			if (data!.length !== resultData.length) {
				start = data!.length;
			}
			const end = start + limit;
			list = [...data, ...resultData.slice(start, end)];
		}
		return new Promise((resolve) => {
			setTimeout(() => {
				resolve(list!);
			}, 1000);
		});
	}, [resultData]);

	const { data, loading, loadData, fullData } = useInfiniteScroll<string[], HTMLDivElement>({
		request: getLoadMoreList,
		ref,
		hasMoreData: useCallback((d?: string[]) => (d || []).length !== resultData.length, [resultData]),
		threshold: 240
	});

	return (<>
		<h2 style={{textAlign: "left"}}>Items List</h2>
		<div ref={ref} style={{ height: 250, overflow: 'auto', border: '1px solid', padding: 12 }}>
			<div>
				{data?.map((item) => (
					<div key={item} style={{ padding: 12, border: '1px solid #f5f5f5' }}>
						item-{Number(item) + 1}
					</div>
				))}
			</div>
			<div style={{ marginTop: 8 }}>
				{!fullData && (
					<button type="button" onClick={loadData} disabled={loading}>
						{loading ? 'Loading more...' : 'Click to load more'}
					</button>
				)}

				{fullData && <span>No more data</span>}
			</div>
		</div>
	</>);
}

Types

UseInfiniteScrollProps

  • @template T - The type of the data returned by each page request.
  • @template E - The scrollable container element type, extending {@link Element}.

Props accepted by useInfiniteScroll.

PropertyTypeRequiredDescription
request(data?: T) => Promise<T>An async function that fetches the next page of data. Receives the accumulated result of all previous requests as an optional argument, enabling cursor-based or offset-based pagination strategies. The resolved value is stored as the new data in {@link UseInfiniteScrollResult}.
refRefObject<E>A React RefObject attached to the scrollable container element to monitor. When the user scrolls within threshold pixels of the bottom of this element, the next page request is triggered automatically.
hasMoreData(data?: T) => booleanA predicate called after each successful request to determine whether more pages are available. Receives the latest resolved data and should return true to allow further requests, or false to stop automatic fetching and set fullData to true in {@link UseInfiniteScrollResult}.
thresholdnumberDistance in pixels from the bottom of the scrollable container at which the next page request is triggered. A value of 0 fires the request exactly when the bottom is reached; higher values trigger it earlier.
onBefore() => voidCalled synchronously before each page request begins. Use this to show a custom loading indicator or perform any pre-request side effects.
onSuccess() => voidCalled after each successful page request. Use this to hide a loading indicator or perform any post-request side effects.
onError(err: unknown) => voidCalled when a page request throws an error. Receives the thrown value, which may be an Error or any other type depending on the request implementation. When omitted, errors are silently swallowed.

UseInfiniteScrollResult

  • @template T - The type of the data returned by each page request.

Return value of {@link useInfiniteScroll}.

PropertyTypeRequiredDescription
dataT \| undefinedThe data returned by the most recent successful request call. undefined before the first page has loaded.
loadingbooleantrue while a request call is currently in-flight, false otherwise. Use this to render a loading indicator.
fullDatabooleantrue when hasMoreData returned false after the most recent request, indicating that all available pages have been loaded and no further automatic requests will be triggered.
updateData(data: T \| ((currentState?: T) => T)) => voidImperatively updates the current data without triggering a new request call. Accepts either a direct replacement value of type T or an updater function that receives the current data and returns the next value. Useful for optimistic updates or local mutations.
loadData() => Promise<void>Manually triggers the next page request using the same logic as the automatic scroll-triggered fetch. Resolves when the request completes, regardless of success or failure.

Released under the MIT License.