Skip to content

Instantly share code, notes, and snippets.

@crazy4groovy
Created December 1, 2023 17:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save crazy4groovy/a0834de49ba1cddf390b34201eda03e9 to your computer and use it in GitHub Desktop.
Save crazy4groovy/a0834de49ba1cddf390b34201eda03e9 to your computer and use it in GitHub Desktop.
A React hook for scroll position tracking (JavaScript)
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
interface ScrollTrackerResult {
isScrollable: boolean;
isAtTop: boolean;
isAtBottom: boolean;
}
function useScrollTracker(ref: React.RefObject<HTMLElement>): ScrollTrackerResult {
const [isScrollable, setIsScrollable] = useState(false);
const [isAtTop, setIsAtTop] = useState(true);
const [isAtBottom, setIsAtBottom] = useState(false);
const getScrollState = useCallback(() => {
if (ref.current) {
const { scrollTop, scrollHeight, clientHeight } = ref.current;
setIsScrollable(scrollHeight > clientHeight);
setIsAtTop(scrollTop === 0);
setIsAtBottom(scrollTop + clientHeight >= scrollHeight);
}
}, [ref]);
const debouncedGetScrollState = useRef(debounce(getScrollState, 100));
useEffect(() => {
const element = ref.current;
if (element) {
element.addEventListener("scroll", debouncedGetScrollState.current);
return () => element.removeEventListener("scroll",
debouncedGetScrollState.current);
}
}, [debouncedGetScrollState]);
const state: ScrollTrackerResult = useMemo(() => ({
isScrollable,
isAtTop,
isAtBottom
}), [isScrollable, isAtTop, isAtBottom]);
return state;
}
function debounce(fn: () => any, delayMs = 100) {
let timeout: any;
return () => {
clearTimeout(timeout);
timeout = setTimeout(fn, delayMs);
}
}
export default useScrollTracker;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment