useDocumentScroll

Published on

This hook is used to execute a callback code when the user scrolls.

useDocumentScroll.tsx
import { useEffect, useState } from 'react';
import { throttle } from 'lodash';

export function useDocumentScroll(
  callback: (params: { previousScrollTop: number; currentScrollTop: number }) => void
) {
  const [, setScrollPosition] = useState(0);
  let previousScrollTop = 0;

  function handleDocumentScroll() {
    const { scrollTop: currentScrollTop } = document.documentElement || document.body;

    setScrollPosition((previousPosition) => {
      previousScrollTop = previousPosition;
      return currentScrollTop;
    });

    callback({ previousScrollTop, currentScrollTop });
  }

  const handleDocumentScrollThrottled = throttle(handleDocumentScroll, 250);

  useEffect(() => {
    window.addEventListener('scroll', handleDocumentScrollThrottled);

    return () => window.removeEventListener('scroll', handleDocumentScrollThrottled);
  }, []);
}

This is how you use the hook in a component to show or hide navbar (like on this site).

navbar.tsx
let timer: any;
const [shouldHideHeader, setShouldHideHeader] = useState(false);

const MINIMUM_SCROLL = 80;
const TIMEOUT_DELAY = 200;

useDocumentScroll((callbackData) => {
  const { previousScrollTop, currentScrollTop } = callbackData;
  const isScrolledDown = previousScrollTop < currentScrollTop;
  const isMinimumScrolled = currentScrollTop > MINIMUM_SCROLL;

  timer = setTimeout(() => {
    setShouldHideHeader(isScrolledDown && isMinimumScrolled);
  }, TIMEOUT_DELAY);
});

useEffect(() => () => clearTimeout(timer));

return (
  <>
    // your html code here. You can use shouldHideHeader variable
  </>

);