DOM
useInfiniteScroll
Triggers loading near the scroll (MDN, opens in a new tab) end.
Demo
live · imported from hookli
- mock item #01
- mock item #02
- mock item #03
- mock item #04
- mock item #05
- mock item #06
- mock item #07
- mock item #08
- loaded
- 8 / 32
- isFetching
- false
scroll the page toward its bottom to load more
import { useCallback, useState } from "react";
import { useInfiniteScroll } from "hookli";
const page = (start: number) =>
Array.from({ length: 10 }, (_, i) => `Item ${start + i + 1}`);
export function Demo() {
const [items, setItems] = useState(() => page(0));
const fetchMoreData = useCallback(
() =>
new Promise<void>((resolve) => {
setItems((prev) => [...prev, ...page(prev.length)]);
resolve();
}),
[],
);
const isFetching = useInfiniteScroll(fetchMoreData);
return (
<div>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
{isFetching && <p>Loading more…</p>}
</div>
);
}Usage
import { useCallback, useState } from "react";
import { useInfiniteScroll } from "hookli";
const page = (start: number) =>
Array.from({ length: 10 }, (_, i) => `Item ${start + i + 1}`);
export function Demo() {
const [items, setItems] = useState(() => page(0));
const fetchMoreData = useCallback(
() =>
new Promise<void>((resolve) => {
setItems((prev) => [...prev, ...page(prev.length)]);
resolve();
}),
[],
);
const isFetching = useInfiniteScroll(fetchMoreData);
return (
<div>
<ul>
{items.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
{isFetching && <p>Loading more…</p>}
</div>
);
}API
useInfiniteScroll(fetchMoreData: () => Promise<void>): booleanParameters
| Name | Type | Default | Description |
|---|---|---|---|
| fetchMoreData | () => Promise<void> | — | Called when the window scroll comes within 500px of the document bottom. Must return a promise — the hook stays in the fetching state until it resolves. |
Returns
| Name | Type | Description |
|---|---|---|
| isFetching | boolean | True while a triggered fetchMoreData promise is pending; blocks re-triggering until it resolves. |
Hook
The implementation, straight from hookli. Copy it, or install the package.
import { useEffect, useState } from "react";
type FetchMoreData = () => Promise<void>;
export const useInfiniteScroll = (fetchMoreData: FetchMoreData): boolean => {
const [isFetching, setIsFetching] = useState(false);
useEffect(() => {
const handleScroll = () => {
const isNearBottom =
window.innerHeight + window.scrollY >=
document.body.offsetHeight - 500;
if (isNearBottom && !isFetching) {
setIsFetching(true);
fetchMoreData().then(() => setIsFetching(false));
}
};
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, [fetchMoreData, isFetching]);
return isFetching;
};