/** * Scroll Tracking Hook * Tracks user scroll depth and sends events to Google Tag Manager */ import { useEffect, useRef } from 'react'; import { trackScrollDepth } from '../services/gtm'; /** * Custom hook to track scroll depth for GTM analytics * * Monitors user scroll behavior and triggers GTM events when the user reaches * specific depth milestones (25%, 50%, 75%, 90%, 100%). Each milestone is * tracked only once per page visit to avoid duplicate events. * * Features: * - Debounced scroll event handling (100ms) for performance * - Tracks depth milestones: 25%, 50%, 75%, 90%, 100% * - Prevents duplicate tracking of the same milestone * - Automatically resets tracking state on route changes * - Checks initial scroll position on mount * * @example * ```tsx * function MyPage() { * useScrollTracking(); * return
...
; * } * ``` * * @remarks * This hook should be used at the page/route level component to ensure * accurate tracking across navigation. The tracking state is automatically * reset when the URL pathname changes. */ export const useScrollTracking = () => { const trackedDepths = useRef(new Set()); useEffect(() => { let scrollTimeout: NodeJS.Timeout; const depths = [25, 50, 75, 90, 100]; const handleScroll = () => { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(() => { const windowHeight = window.innerHeight; const documentHeight = document.documentElement.scrollHeight; const scrollTop = window.scrollY; const scrollPercentage = Math.round( ((scrollTop + windowHeight) / documentHeight) * 100 ); // Track each depth milestone only once depths.forEach(depth => { if (scrollPercentage >= depth && !trackedDepths.current.has(depth)) { trackedDepths.current.add(depth); trackScrollDepth(depth); } }); }, 100); // Debounce scroll events }; window.addEventListener('scroll', handleScroll); // Check initial scroll position handleScroll(); return () => { window.removeEventListener('scroll', handleScroll); clearTimeout(scrollTimeout); }; }, []); // Reset tracked depths when route changes useEffect(() => { trackedDepths.current.clear(); }, [window.location.pathname]); };