JAVASCRIPT
Tracking Scroll Position with `useScrollPosition`
A custom React hook to track the current vertical and horizontal scroll position of the window or a specified element, useful for scroll-based UI effects.
import { useState, useEffect, useRef } from 'react';
const getScrollPosition = (element) => {
if (!element || element === window) {
return { x: window.scrollX, y: window.scrollY };
}
return { x: element.scrollLeft, y: element.scrollTop };
};
export const useScrollPosition = (
elementRef = null, // Can be window or a ref to a DOM element
wait = 100 // Throttling delay
) => {
const [scrollPosition, setScrollPosition] = useState(getScrollPosition(elementRef ? elementRef.current : window));
const timeoutRef = useRef(null);
const lastScrollRef = useRef(0);
useEffect(() => {
const currentElement = elementRef ? elementRef.current : window;
if (!currentElement) return;
const handleScroll = () => {
const now = Date.now();
const lastExecution = lastScrollRef.current;
if (now - lastExecution > wait) {
setScrollPosition(getScrollPosition(currentElement));
lastScrollRef.current = now;
} else {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
setScrollPosition(getScrollPosition(currentElement));
lastScrollRef.current = Date.now();
timeoutRef.current = null;
}, wait - (now - lastExecution));
}
};
currentElement.addEventListener('scroll', handleScroll);
return () => {
currentElement.removeEventListener('scroll', handleScroll);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
};
}, [elementRef, wait]);
return scrollPosition;
};
How it works: The `useScrollPosition` hook tracks the scroll position (x, y coordinates) of the window or a specific DOM element. It takes an optional `elementRef` (for a DOM element) and a `wait` time for throttling. This hook efficiently updates the scroll position state by ensuring the update function is called at most once within the specified `wait` period, preventing excessive re-renders during scrolling. It's ideal for dynamic headers, scroll-to-top buttons, or lazy loading components.