@mirawision/reactive-hooks
Version:
A comprehensive collection of 50+ React hooks for state management, UI interactions, device APIs, async operations, drag & drop, audio/speech, and more. Full TypeScript support with SSR safety.
75 lines (74 loc) • 2.43 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.useInfiniteScroll = useInfiniteScroll;
const react_1 = require("react");
/**
* A hook that implements infinite scrolling using Intersection Observer.
* Triggers loadMore when the target element becomes visible.
*
* @param opts Configuration options
* @returns Object with ref callback, loading state, and error state
*
* @example
* const { ref, loading } = useInfiniteScroll({
* hasMore: true,
* loadMore: () => fetchNextPage(),
* });
*
* return (
* <div>
* {items.map(item => <Item key={item.id} {...item} />)}
* <div ref={ref}>{loading ? 'Loading...' : null}</div>
* </div>
* );
*/
function useInfiniteScroll(opts) {
const [loading, setLoading] = (0, react_1.useState)(false);
const [error, setError] = (0, react_1.useState)(null);
// Refs for cleanup and state tracking
const observerRef = (0, react_1.useRef)();
const loadingRef = (0, react_1.useRef)(loading);
loadingRef.current = loading;
// Cleanup function
const cleanup = (0, react_1.useCallback)(() => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = undefined;
}
}, []);
// Load more function with loading state
const handleLoadMore = (0, react_1.useCallback)(async () => {
if (loadingRef.current || !opts.hasMore)
return;
try {
setLoading(true);
setError(null);
await opts.loadMore();
}
catch (err) {
setError(err);
}
finally {
setLoading(false);
}
}, [opts.hasMore, opts.loadMore]);
// Ref callback to observe element
const ref = (0, react_1.useCallback)((element) => {
cleanup();
if (!element || !opts.hasMore)
return;
// Create new observer
const observer = new IntersectionObserver((entries) => {
const [entry] = entries;
if (entry.isIntersecting) {
handleLoadMore();
}
}, {
rootMargin: opts.rootMargin ?? '50px',
threshold: opts.threshold ?? 0,
});
observer.observe(element);
observerRef.current = observer;
}, [opts.hasMore, opts.rootMargin, opts.threshold, handleLoadMore, cleanup]);
return { ref, loading, error };
}