import React, { useEffect, useRef, useCallback, useMemo, useState } from 'react'; import { ExtendedDeviceInfo } from './real-time-device-monitor'; import { useGracefulDegradation } from './edge-case-handler'; // Performance optimization configuration export interface PerformanceConfig { enableVirtualization: boolean; enableLazyLoading: boolean; enableMemoryManagement: boolean; enableRAFOptimization: boolean; enableComputationThrottling: boolean; enableGarbageCollection: boolean; maxCacheSize: number; memoryThreshold: number; throttleMs: number; virtualScrollBuffer: number; } const DEFAULT_PERFORMANCE_CONFIG: PerformanceConfig = { enableVirtualization: true, enableLazyLoading: true, enableMemoryManagement: true, enableRAFOptimization: true, enableComputationThrottling: true, enableGarbageCollection: true, maxCacheSize: 100, memoryThreshold: 0.8, throttleMs: 16, // 60fps virtualScrollBuffer: 5 }; // Performance metrics export interface PerformanceMetrics { renderTime: number; updateCount: number; memoryUsage: number; frameRate: number; cacheHitRatio: number; gcCount: number; timestamp: number; } // Memory management utilities class MemoryManager { private static instance: MemoryManager; private cache = new Map(); private maxSize: number; private accessOrder = new Map(); constructor(maxSize: number = 100) { this.maxSize = maxSize; } static getInstance(maxSize?: number): MemoryManager { if (!MemoryManager.instance) { MemoryManager.instance = new MemoryManager(maxSize); } return MemoryManager.instance; } set(key: string, value: any): void { // LRU eviction if (this.cache.size >= this.maxSize && !this.cache.has(key)) { const oldestKey = [...this.accessOrder.entries()] .sort(([, a], [, b]) => a - b)[0][0]; this.cache.delete(oldestKey); this.accessOrder.delete(oldestKey); } this.cache.set(key, value); this.accessOrder.set(key, Date.now()); } get(key: string): any { if (this.cache.has(key)) { this.accessOrder.set(key, Date.now()); return this.cache.get(key); } return undefined; } clear(): void { this.cache.clear(); this.accessOrder.clear(); } getStats() { return { size: this.cache.size, maxSize: this.maxSize, usage: this.cache.size / this.maxSize }; } }// Performance monitoring hook export const usePerformanceOptimizer = ( config: Partial = {}, deviceInfo: ExtendedDeviceInfo ) => { const finalConfig = { ...DEFAULT_PERFORMANCE_CONFIG, ...config }; const { shouldDisableFeature } = useGracefulDegradation(deviceInfo); const [metrics, setMetrics] = useState({ renderTime: 0, updateCount: 0, memoryUsage: 0, frameRate: 60, cacheHitRatio: 0, gcCount: 0, timestamp: Date.now() }); const memoryManagerRef = useRef(); const renderStartRef = useRef(0); const frameCountRef = useRef(0); const lastFrameTimeRef = useRef(0); const rafIdRef = useRef(0); // Initialize memory manager useEffect(() => { memoryManagerRef.current = MemoryManager.getInstance(finalConfig.maxCacheSize); }, [finalConfig.maxCacheSize]); // RAF-based frame rate monitoring const monitorFrameRate = useCallback(() => { const now = performance.now(); frameCountRef.current++; if (lastFrameTimeRef.current) { const deltaTime = now - lastFrameTimeRef.current; const currentFPS = 1000 / deltaTime; setMetrics(prev => ({ ...prev, frameRate: Math.round(currentFPS * 0.1 + prev.frameRate * 0.9), // Smooth average timestamp: Date.now() })); } lastFrameTimeRef.current = now; if (finalConfig.enableRAFOptimization && !shouldDisableFeature('performance-monitoring')) { rafIdRef.current = requestAnimationFrame(monitorFrameRate); } }, [finalConfig.enableRAFOptimization, shouldDisableFeature]); // Memory usage monitoring const checkMemoryUsage = useCallback(() => { if ('memory' in performance) { const memory = (performance as any).memory; const usage = memory.usedJSHeapSize / memory.totalJSHeapSize; setMetrics(prev => ({ ...prev, memoryUsage: usage })); // Trigger garbage collection if threshold exceeded if (usage > finalConfig.memoryThreshold && finalConfig.enableGarbageCollection) { memoryManagerRef.current?.clear(); // Suggest manual GC if available (Chrome DevTools) if (typeof window !== 'undefined' && 'gc' in window) { (window as any).gc(); } setMetrics(prev => ({ ...prev, gcCount: prev.gcCount + 1 })); } } }, [finalConfig.memoryThreshold, finalConfig.enableGarbageCollection]); // Render time measurement const measureRenderTime = useCallback(() => { renderStartRef.current = performance.now(); }, []); const recordRenderTime = useCallback(() => { if (renderStartRef.current) { const renderTime = performance.now() - renderStartRef.current; setMetrics(prev => ({ ...prev, renderTime, updateCount: prev.updateCount + 1 })); } }, []); // Throttled computation utility const throttledComputation = useCallback( (computation: () => void, delay: number = finalConfig.throttleMs) => { if (!finalConfig.enableComputationThrottling) { computation(); return; } const throttleKey = computation.toString(); const cached = memoryManagerRef.current?.get(`throttle_${throttleKey}`); if (cached && Date.now() - cached < delay) { return; // Skip computation } memoryManagerRef.current?.set(`throttle_${throttleKey}`, Date.now()); computation(); }, [finalConfig.throttleMs, finalConfig.enableComputationThrottling] ); // Cache utility with hit ratio tracking const cache = useMemo(() => ({ get: (key: string) => { const hit = memoryManagerRef.current?.get(key); const stats = memoryManagerRef.current?.getStats(); if (stats) { setMetrics(prev => ({ ...prev, cacheHitRatio: hit ? (prev.cacheHitRatio * 0.9 + 0.1) : (prev.cacheHitRatio * 0.9) })); } return hit; }, set: (key: string, value: any) => { memoryManagerRef.current?.set(key, value); }, clear: () => { memoryManagerRef.current?.clear(); } }), []); // Start monitoring useEffect(() => { if (!shouldDisableFeature('performance-monitoring')) { rafIdRef.current = requestAnimationFrame(monitorFrameRate); const memoryInterval = setInterval(checkMemoryUsage, 5000); // Check every 5s return () => { cancelAnimationFrame(rafIdRef.current); clearInterval(memoryInterval); }; } }, [monitorFrameRate, checkMemoryUsage, shouldDisableFeature]); return { metrics, measureRenderTime, recordRenderTime, throttledComputation, cache, memoryManager: memoryManagerRef.current, isOptimized: !shouldDisableFeature('performance-monitoring') }; }; // Performance optimized component wrapper interface PerformanceWrapperProps { children: React.ReactNode; config?: Partial; deviceInfo: ExtendedDeviceInfo; enableDebug?: boolean; } export const PerformanceWrapper: React.FC = ({ children, config, deviceInfo, enableDebug = false }) => { const { metrics, measureRenderTime, recordRenderTime, isOptimized } = usePerformanceOptimizer(config, deviceInfo); useEffect(() => { measureRenderTime(); }); useEffect(() => { recordRenderTime(); }); return ( <> {children} {enableDebug && process.env.NODE_ENV === 'development' && isOptimized && ( )} ); };// Performance debug panel interface PerformanceDebugPanelProps { metrics: PerformanceMetrics; } const PerformanceDebugPanel: React.FC = ({ metrics }) => { const [isExpanded, setIsExpanded] = useState(false); const getStatusColor = (value: number, thresholds: [number, number]) => { if (value < thresholds[0]) return '#00aa00'; // Good if (value < thresholds[1]) return '#ffbb00'; // Warning return '#ff4444'; // Critical }; const formatMemory = (bytes: number) => { if (bytes < 1024) return `${bytes}B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`; return `${(bytes / (1024 * 1024)).toFixed(1)}MB`; }; return (
setIsExpanded(!isExpanded)} > Performance Monitor {isExpanded ? '▼' : '▶'}
{isExpanded && (
FPS: {metrics.frameRate.toFixed(1)}
Render Time: {metrics.renderTime.toFixed(2)}ms
Memory Usage: {(metrics.memoryUsage * 100).toFixed(1)}%
Cache Hit Ratio: {(metrics.cacheHitRatio * 100).toFixed(1)}%
Updates: {metrics.updateCount}
GC Count: {metrics.gcCount}
Last updated: {new Date(metrics.timestamp).toLocaleTimeString()}
)}
); }; // Virtualized list component for performance interface VirtualizedListProps { items: any[]; itemHeight: number; containerHeight: number; renderItem: (item: any, index: number) => React.ReactNode; overscan?: number; } export const VirtualizedList: React.FC = ({ items, itemHeight, containerHeight, renderItem, overscan = 5 }) => { const [scrollTop, setScrollTop] = useState(0); const containerRef = useRef(null); const startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - overscan); const endIndex = Math.min( items.length - 1, Math.floor((scrollTop + containerHeight) / itemHeight) + overscan ); const visibleItems = items.slice(startIndex, endIndex + 1); const totalHeight = items.length * itemHeight; const offsetY = startIndex * itemHeight; const handleScroll = useCallback((e: React.UIEvent) => { setScrollTop(e.currentTarget.scrollTop); }, []); return (
{visibleItems.map((item, index) => (
{renderItem(item, startIndex + index)}
))}
); }; // Lazy loaded component interface LazyComponentProps { loader: () => Promise>; fallback?: React.ComponentType; threshold?: number; } export const LazyComponent: React.FC = ({ loader, fallback: Fallback = () =>
Loading...
, threshold = 100 }) => { const [Component, setComponent] = useState | null>(null); const [isVisible, setIsVisible] = useState(false); const ref = useRef(null); useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setIsVisible(true); observer.disconnect(); } }, { rootMargin: `${threshold}px` } ); if (ref.current) { observer.observe(ref.current); } return () => observer.disconnect(); }, [threshold]); useEffect(() => { if (isVisible && !Component) { loader().then(setComponent); } }, [isVisible, Component, loader]); return (
{Component ? : }
); }; export default PerformanceWrapper;