You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
overseerr/src/hooks/useVerticalScroll.ts

81 lines
2.0 KiB

import { debounce } from 'lodash';
import type { MutableRefObject } from 'react';
import { useEffect, useRef, useState } from 'react';
const IS_SCROLLING_CHECK_THROTTLE = 200;
const BUFFER_HEIGHT = 200;
/**
* useVerticalScroll is a custom hook to handle infinite scrolling
*
* @param callback Callback is executed when page reaches bottom
* @param shouldFetch Disables callback if true
*/
const useVerticalScroll = (
callback: () => void,
shouldFetch: boolean
): boolean => {
const [isScrolling, setScrolling] = useState(false);
type SetTimeoutReturnType = ReturnType<typeof setTimeout>;
const scrollingTimer: MutableRefObject<SetTimeoutReturnType | undefined> =
useRef();
const runCallback = () => {
if (shouldFetch) {
const scrollTop = Math.max(
window.pageYOffset,
document.documentElement.scrollTop,
document.body.scrollTop
);
if (
window.innerHeight + scrollTop >=
document.documentElement.offsetHeight - BUFFER_HEIGHT
) {
callback();
}
}
};
const debouncedCallback = debounce(runCallback, 50);
useEffect(() => {
runCallback();
});
useEffect(() => {
const onScroll = () => {
if (scrollingTimer.current !== undefined) {
clearTimeout(scrollingTimer.current);
}
if (!isScrolling) {
setScrolling(true);
}
scrollingTimer.current = setTimeout(() => {
setScrolling(false);
}, IS_SCROLLING_CHECK_THROTTLE);
debouncedCallback();
};
const onResize = () => {
debouncedCallback();
};
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onResize, { passive: true });
return () => {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onResize);
if (scrollingTimer.current !== undefined) {
clearTimeout(scrollingTimer.current);
}
};
});
return isScrolling;
};
export default useVerticalScroll;