# Critical Fixes - Quick Reference Guide ## 🚨 Top 5 Critical Fixes (Do These First) ### 1. Fix useNotifications Memory Leak ⚠️ CRITICAL **File**: `hooks/use-notifications.ts` **Line**: 239-255 **Problem**: Cleanup function not properly placed, causing memory leaks **Quick Fix**: ```typescript useEffect(() => { if (status !== 'authenticated' || !session?.user) return; isMountedRef.current = true; // Initial fetch fetchNotificationCount(true); fetchNotifications(); // Start polling with proper cleanup const intervalId = setInterval(() => { if (isMountedRef.current) { debouncedFetchCount(); } }, POLLING_INTERVAL); // ✅ Proper cleanup return () => { isMountedRef.current = false; clearInterval(intervalId); }; }, [status, session?.user?.id]); // ✅ Only primitive dependencies ``` --- ### 2. Fix Notification Badge Double Fetching ⚠️ CRITICAL **File**: `components/notification-badge.tsx` **Lines**: 65-70, 82-87, 92-99 **Problem**: Three different places trigger the same fetch simultaneously **Quick Fix**: ```typescript // Add at top of component const fetchInProgressRef = useRef(false); const lastFetchRef = useRef(0); const FETCH_COOLDOWN = 1000; // 1 second cooldown const manualFetch = async () => { const now = Date.now(); // Prevent duplicate fetches if (fetchInProgressRef.current) { console.log('[NOTIFICATION_BADGE] Fetch already in progress'); return; } // Cooldown check if (now - lastFetchRef.current < FETCH_COOLDOWN) { console.log('[NOTIFICATION_BADGE] Too soon since last fetch'); return; } fetchInProgressRef.current = true; lastFetchRef.current = now; try { await fetchNotifications(1, 10); } finally { fetchInProgressRef.current = false; } }; // Remove duplicate useEffect hooks, keep only one: useEffect(() => { if (isOpen && status === 'authenticated') { manualFetch(); } }, [isOpen, status]); // Only this one ``` --- ### 3. Fix Redis KEYS Performance Issue ⚠️ CRITICAL **File**: `lib/services/notifications/notification-service.ts` **Line**: 293 **Problem**: `redis.keys()` blocks Redis and is O(N) **Quick Fix**: ```typescript // BEFORE (Line 293) const listKeys = await redis.keys(listKeysPattern); if (listKeys.length > 0) { await redis.del(...listKeys); } // AFTER (Use SCAN) const listKeys: string[] = []; let cursor = '0'; do { const [nextCursor, keys] = await redis.scan( cursor, 'MATCH', listKeysPattern, 'COUNT', 100 ); cursor = nextCursor; if (keys.length > 0) { listKeys.push(...keys); } } while (cursor !== '0'); if (listKeys.length > 0) { await redis.del(...listKeys); } ``` --- ### 4. Fix Widget Interval Cleanup ⚠️ HIGH **Files**: - `components/calendar.tsx` (line 70) - `components/parole.tsx` (line 83) - `components/calendar/calendar-widget.tsx` (line 110) **Problem**: Intervals may not be cleaned up properly **Quick Fix Pattern**: ```typescript // BEFORE useEffect(() => { fetchEvents(); const intervalId = setInterval(fetchEvents, 300000); return () => clearInterval(intervalId); }, []); // ❌ Missing dependencies // AFTER useEffect(() => { if (status !== 'authenticated') return; const fetchEvents = async () => { // ... fetch logic }; fetchEvents(); // Initial fetch const intervalId = setInterval(fetchEvents, 300000); return () => { clearInterval(intervalId); }; }, [status]); // ✅ Proper dependencies ``` --- ### 5. Fix useEffect Infinite Loop Risk ⚠️ HIGH **File**: `hooks/use-notifications.ts` **Line**: 255 **Problem**: Function dependencies cause infinite re-renders **Quick Fix**: ```typescript // Remove function dependencies, use refs for stable references const fetchNotificationCountRef = useRef(fetchNotificationCount); const fetchNotificationsRef = useRef(fetchNotifications); useEffect(() => { fetchNotificationCountRef.current = fetchNotificationCount; fetchNotificationsRef.current = fetchNotifications; }); useEffect(() => { if (status !== 'authenticated' || !session?.user) return; isMountedRef.current = true; fetchNotificationCountRef.current(true); fetchNotificationsRef.current(); const intervalId = setInterval(() => { if (isMountedRef.current) { fetchNotificationCountRef.current(); } }, POLLING_INTERVAL); return () => { isMountedRef.current = false; clearInterval(intervalId); }; }, [status, session?.user?.id]); // ✅ Only primitive values ``` --- ## 🔧 Additional Quick Wins ### 6. Add Request Deduplication Utility **Create**: `lib/utils/request-deduplication.ts` ```typescript const pendingRequests = new Map>(); export function deduplicateRequest( key: string, requestFn: () => Promise ): Promise { if (pendingRequests.has(key)) { return pendingRequests.get(key)!; } const promise = requestFn().finally(() => { pendingRequests.delete(key); }); pendingRequests.set(key, promise); return promise; } ``` **Usage**: ```typescript const data = await deduplicateRequest( `notifications-${userId}`, () => fetch('/api/notifications').then(r => r.json()) ); ``` --- ### 7. Extract Magic Numbers to Constants **Create**: `lib/constants/intervals.ts` ```typescript export const INTERVALS = { NOTIFICATION_POLLING: 60000, // 1 minute CALENDAR_REFRESH: 300000, // 5 minutes PAROLE_POLLING: 30000, // 30 seconds MIN_FETCH_INTERVAL: 5000, // 5 seconds FETCH_COOLDOWN: 1000, // 1 second } as const; ``` --- ### 8. Add Error Retry Logic **Create**: `lib/utils/retry.ts` ```typescript export async function retry( fn: () => Promise, maxAttempts = 3, delay = 1000 ): Promise { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxAttempts) throw error; await new Promise(resolve => setTimeout(resolve, delay * attempt)); } } throw new Error('Max retry attempts reached'); } ``` --- ## 📋 Testing Checklist After applying fixes, test: - [ ] No memory leaks (check browser DevTools Memory tab) - [ ] No duplicate API calls (check Network tab) - [ ] Intervals are cleaned up (check console for errors) - [ ] No infinite loops (check React DevTools Profiler) - [ ] Redis performance (check response times) - [ ] Error handling works (test with network offline) --- ## 🎯 Priority Order 1. **Fix 1** (Memory Leak) - Do immediately 2. **Fix 2** (Double Fetching) - Do immediately 3. **Fix 3** (Redis KEYS) - Do immediately 4. **Fix 4** (Widget Cleanup) - Do within 24 hours 5. **Fix 5** (Infinite Loop) - Do within 24 hours 6. **Quick Wins** - Do within 1 week --- *Last Updated: Critical fixes quick reference*