Commit 4bf71e9
authored
🤖 fix: reduce ProjectSidebar re-renders during streaming (#218)
## Problem
ProjectSidebar was re-rendering on every streaming delta when watching a
chat in another workspace, causing unnecessary CPU usage and UI churn.
## Solution
**Stabilize unreadStatus Map identity:**
- Modified `useUnreadTracking` to only return a new Map reference when
unread boolean values actually change
- Prevents ProjectSidebar re-renders during streaming when unread state
is stable
- Uses ref-based deep comparison to maintain Map identity across renders
**Code changes:**
```typescript
// Before: New Map created on every workspaceStates or lastReadMap change
const unreadStatus = useMemo(() => {
const map = new Map<string, boolean>();
// ... calculate unread status
return map; // New identity every time
}, [workspaceStates, lastReadMap]);
// After: Map identity preserved when values unchanged
const unreadStatus = useMemo(() => {
const next = new Map<string, boolean>();
// ... calculate unread status
// Compare with previous - only return new Map if values changed
const prev = unreadStatusRef.current;
if (prev.size === next.size) {
let same = true;
for (const [k, v] of next) {
if (prev.get(k) !== v) {
same = false;
break;
}
}
if (same) return prev; // Maintain identity
}
unreadStatusRef.current = next;
return next;
}, [workspaceStates, lastReadMap]);
```
## Testing
Manual verification:
- ✅ Streaming in workspace A while viewing workspace B no longer causes
sidebar re-renders
- ✅ Unread status updates correctly on workspace switches and stream
completion
- ✅ Toggle unread function works correctly
- ✅ Drag-and-drop project reordering remains smooth
## Impact
**Fixed:**
- ProjectSidebar no longer re-renders on every streaming delta in
non-selected workspaces
- Reduced unnecessary CPU usage during streaming operations
**Not fixed (separate issue):**
- ProjectSidebar still re-renders every 3 seconds due to
`useGitStatus()` call at line 611
- Will be addressed in a follow-up PR by removing the direct hook call
and passing git status as a prop
## Notes
The comparison logic handles all edge cases correctly:
- Empty maps
- Size changes (added/removed workspaces)
- Key changes (different workspace IDs)
- Value changes (unread status flips)
_Generated with `cmux`_1 parent 720ec9d commit 4bf71e9
2 files changed
+22
-5
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
34 | | - | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
| 76 | + | |
76 | 77 | | |
77 | | - | |
| 78 | + | |
78 | 79 | | |
79 | 80 | | |
80 | 81 | | |
81 | 82 | | |
82 | | - | |
| 83 | + | |
83 | 84 | | |
84 | 85 | | |
85 | 86 | | |
| |||
92 | 93 | | |
93 | 94 | | |
94 | 95 | | |
95 | | - | |
| 96 | + | |
96 | 97 | | |
97 | 98 | | |
98 | | - | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
99 | 116 | | |
100 | 117 | | |
101 | 118 | | |
| |||
0 commit comments