useScrollPosition
Prompt
Create a custom hook useScrollPosition that tracks the window's scroll position. It should return an object { x, y } that updates whenever the user scrolls.
Playground
Start by creating a state variable with useState to store the scroll position as an object with x and y properties.
Use the useEffect hook to add an event listener for the 'scroll' event on the window object.
Remember to remove the event listener in the cleanup function returned by useEffect to prevent memory leaks.
Solution
Explanation
If you've already built useHover, this will feel very familiar. Same recipe: hold some state, listen for an event, update the state, clean up.
export const useScrollPosition = () => {
const [scrollPosition, setScrollPosition] = useState({
x: 0,
y: 0,
});
useEffect(() => {
function handleScroll() {
setScrollPosition({
x: Math.round(window.scrollX),
y: Math.round(window.scrollY),
});
}
window.addEventListener('scroll', handleScroll);
return () =>
window.removeEventListener('scroll', handleScroll);
}, []);
return scrollPosition;
};We store the scroll position as { x, y } in state. When the component mounts, we add a scroll listener to the window. Every time the user scrolls, handleScroll fires and updates the state with the current position. When the component unmounts, we remove the listener.
The empty dependency array [] means we set this up once and forget about it. The listener stays active for the entire lifetime of the component.
You might notice Math.round() around the scroll values. That's because browsers sometimes return fractional pixels for scroll positions (like 142.66667). Rounding gives cleaner numbers that are easier to work with and display.
Where would you use this? Sticky headers that appear after scrolling past a certain point, scroll progress bars, "back to top" buttons that fade in when you've scrolled far enough, parallax effects, or lazy-loading content as the user scrolls down.