Problem
projectsWithSessions is local useState in DraggableProjectTreeView.tsx (~2500 lines). Every session status change creates a new array reference, which has caused infinite re-render loops in multiple useEffect hooks that depend on it. We've patched this twice now with refs:
22ba70c - Diff panel infinite re-render (same pattern)
453869d - DraggableProjectTreeView infinite re-render (3 effects)
The ref pattern works but is a symptom fix — the underlying issue is structural.
Root Cause
Event handlers and effects need access to the latest projectsWithSessions data, but including it in dependency arrays causes cascading re-renders because every update produces a new array reference. We currently work around this by syncing a useRef on every render, which is fragile and easy to forget when adding new effects.
Proposed Solution
Move projectsWithSessions into a Zustand store. This gives us:
- Shallow selectors — components only re-render when the specific slice they subscribe to changes, not on every array reference change
getState() — event handlers can read current state directly without closures or refs
- Single source of truth — no more local state + ref syncing pattern
- Eliminates the class of bug entirely — new effects/handlers can't accidentally depend on a stale or re-triggering reference
Scope
- Extract
projectsWithSessions and archivedProjectsWithSessions state + all mutation logic from DraggableProjectTreeView.tsx into a new Zustand store (or extend sessionStore)
- Update all IPC event handlers that call
setProjectsWithSessions to use store actions instead
- Remove the
projectsWithSessionsRef / runningProjectIdRef workarounds
- Components subscribe to specific slices with shallow comparison
Notes
This would also be a step toward breaking up the 2500-line DraggableProjectTreeView component, since the data layer would be decoupled from the UI.
Problem
projectsWithSessionsis localuseStateinDraggableProjectTreeView.tsx(~2500 lines). Every session status change creates a new array reference, which has caused infinite re-render loops in multipleuseEffecthooks that depend on it. We've patched this twice now with refs:22ba70c- Diff panel infinite re-render (same pattern)453869d- DraggableProjectTreeView infinite re-render (3 effects)The ref pattern works but is a symptom fix — the underlying issue is structural.
Root Cause
Event handlers and effects need access to the latest
projectsWithSessionsdata, but including it in dependency arrays causes cascading re-renders because every update produces a new array reference. We currently work around this by syncing auseRefon every render, which is fragile and easy to forget when adding new effects.Proposed Solution
Move
projectsWithSessionsinto a Zustand store. This gives us:getState()— event handlers can read current state directly without closures or refsScope
projectsWithSessionsandarchivedProjectsWithSessionsstate + all mutation logic fromDraggableProjectTreeView.tsxinto a new Zustand store (or extendsessionStore)setProjectsWithSessionsto use store actions insteadprojectsWithSessionsRef/runningProjectIdRefworkaroundsNotes
This would also be a step toward breaking up the 2500-line
DraggableProjectTreeViewcomponent, since the data layer would be decoupled from the UI.