Files
Portfolio/src/hooks/useScrollTracking.ts
T

79 lines
2.3 KiB
TypeScript

/**
* 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 <div>...</div>;
* }
* ```
*
* @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<number>());
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]);
};