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

308 lines
6.7 KiB
Markdown

# 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<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**:
```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<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**:
```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<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*