NeahNew/CRITICAL_FIXES_QUICK_REFERENCE.md
2026-01-06 13:02:07 +01:00

6.7 KiB

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:

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:

// Add at top of component
const fetchInProgressRef = useRef(false);
const lastFetchRef = useRef<number>(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:

// 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:

// 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:

// 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

const pendingRequests = new Map<string, Promise<any>>();

export function deduplicateRequest<T>(
  key: string,
  requestFn: () => Promise<T>
): Promise<T> {
  if (pendingRequests.has(key)) {
    return pendingRequests.get(key)!;
  }
  
  const promise = requestFn().finally(() => {
    pendingRequests.delete(key);
  });
  
  pendingRequests.set(key, promise);
  return promise;
}

Usage:

const data = await deduplicateRequest(
  `notifications-${userId}`,
  () => fetch('/api/notifications').then(r => r.json())
);

7. Extract Magic Numbers to Constants

Create: lib/constants/intervals.ts

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

export async function retry<T>(
  fn: () => Promise<T>,
  maxAttempts = 3,
  delay = 1000
): Promise<T> {
  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