Spaces:
Paused
Paused
| import { useState, useEffect, useRef, useCallback } from 'react'; | |
| interface UseLazyWidgetOptions { | |
| rootMargin?: string; | |
| threshold?: number; | |
| delay?: number; | |
| } | |
| export function useLazyWidget(options: UseLazyWidgetOptions = {}) { | |
| const { rootMargin = '100px', threshold = 0, delay = 0 } = options; | |
| const [isVisible, setIsVisible] = useState(false); | |
| const [hasLoaded, setHasLoaded] = useState(false); | |
| const ref = useRef<HTMLDivElement>(null); | |
| useEffect(() => { | |
| const element = ref.current; | |
| if (!element) return; | |
| const observer = new IntersectionObserver( | |
| ([entry]) => { | |
| if (entry.isIntersecting) { | |
| if (delay > 0) { | |
| setTimeout(() => { | |
| setIsVisible(true); | |
| setHasLoaded(true); | |
| }, delay); | |
| } else { | |
| setIsVisible(true); | |
| setHasLoaded(true); | |
| } | |
| observer.unobserve(element); | |
| } | |
| }, | |
| { rootMargin, threshold } | |
| ); | |
| observer.observe(element); | |
| return () => observer.disconnect(); | |
| }, [rootMargin, threshold, delay]); | |
| return { ref, isVisible, hasLoaded }; | |
| } | |
| // Virtualization hook for large lists | |
| export function useVirtualList<T>( | |
| items: T[], | |
| itemHeight: number, | |
| containerHeight: number, | |
| overscan: number = 3 | |
| ) { | |
| const [scrollTop, setScrollTop] = useState(0); | |
| const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan); | |
| const endIndex = Math.min( | |
| items.length - 1, | |
| Math.ceil((scrollTop + containerHeight) / itemHeight) + overscan | |
| ); | |
| const visibleItems = items.slice(startIndex, endIndex + 1).map((item, index) => ({ | |
| item, | |
| index: startIndex + index, | |
| style: { | |
| position: 'absolute' as const, | |
| top: (startIndex + index) * itemHeight, | |
| height: itemHeight, | |
| width: '100%', | |
| }, | |
| })); | |
| const totalHeight = items.length * itemHeight; | |
| const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => { | |
| setScrollTop(e.currentTarget.scrollTop); | |
| }, []); | |
| return { | |
| visibleItems, | |
| totalHeight, | |
| handleScroll, | |
| startIndex, | |
| endIndex, | |
| }; | |
| } | |
| // Performance monitoring hook | |
| export function usePerformanceMonitor(componentName: string) { | |
| const renderCount = useRef(0); | |
| const lastRenderTime = useRef(performance.now()); | |
| useEffect(() => { | |
| renderCount.current++; | |
| const now = performance.now(); | |
| const delta = now - lastRenderTime.current; | |
| lastRenderTime.current = now; | |
| if (process.env.NODE_ENV === 'development' && delta > 16) { | |
| console.warn(`[Performance] ${componentName} render took ${delta.toFixed(2)}ms (render #${renderCount.current})`); | |
| } | |
| }); | |
| return { renderCount: renderCount.current }; | |
| } | |