Skip to content

Instantly share code, notes, and snippets.

@crazy4groovy
Created December 1, 2023 17:16
Show Gist options
  • Save crazy4groovy/a0834de49ba1cddf390b34201eda03e9 to your computer and use it in GitHub Desktop.
Save crazy4groovy/a0834de49ba1cddf390b34201eda03e9 to your computer and use it in GitHub Desktop.

Revisions

  1. crazy4groovy revised this gist Dec 1, 2023. No changes.
  2. crazy4groovy created this gist Dec 1, 2023.
    54 changes: 54 additions & 0 deletions useScrollTracker.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    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;