Skip to content

Commit 72ad60b

Browse files
committed
fix(code): use stable empty references in session selector hooks
Zustand selectors use === to decide whether to re-render subscribers. Returning `new Map()` or `[]` on every call creates a fresh reference each time, causing every subscriber to re-render on every store update even when nothing relevant changed. This matters during agent streaming where the session store updates on every event.
1 parent 26e19a4 commit 72ad60b

1 file changed

Lines changed: 19 additions & 12 deletions

File tree

apps/code/src/renderer/features/sessions/hooks/useSession.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ import {
1616
} from "../stores/sessionStore";
1717
import type { PermissionRequest } from "../utils/parseSessionLogs";
1818

19+
// Stable references for fallback values — returning the same object on every
20+
// call prevents Zustand's === check from triggering spurious re-renders.
21+
const EMPTY_MAP = new Map<string, PermissionRequest>();
22+
const EMPTY_COMMANDS: AvailableCommand[] = [];
23+
const EMPTY_QUEUE: QueuedMessage[] = [];
24+
const EMPTY_OPTIMISTIC: OptimisticItem[] = [];
25+
1926
export const useSessions = () => useSessionStore((s) => s.sessions);
2027

2128
/** O(1) lookup using taskIdIndex */
@@ -33,11 +40,11 @@ export const useAvailableCommandsForTask = (
3340
taskId: string | undefined,
3441
): AvailableCommand[] => {
3542
return useSessionStore((s) => {
36-
if (!taskId) return [];
43+
if (!taskId) return EMPTY_COMMANDS;
3744
const taskRunId = s.taskIdIndex[taskId];
38-
if (!taskRunId) return [];
45+
if (!taskRunId) return EMPTY_COMMANDS;
3946
const session = s.sessions[taskRunId];
40-
if (!session?.events) return [];
47+
if (!session?.events) return EMPTY_COMMANDS;
4148
return extractAvailableCommandsFromEvents(session.events);
4249
});
4350
};
@@ -68,11 +75,11 @@ export const usePendingPermissionsForTask = (
6875
taskId: string | undefined,
6976
): Map<string, PermissionRequest> => {
7077
return useSessionStore((s) => {
71-
if (!taskId) return new Map();
78+
if (!taskId) return EMPTY_MAP;
7279
const taskRunId = s.taskIdIndex[taskId];
73-
if (!taskRunId) return new Map();
80+
if (!taskRunId) return EMPTY_MAP;
7481
const session = s.sessions[taskRunId];
75-
return session?.pendingPermissions ?? new Map();
82+
return session?.pendingPermissions ?? EMPTY_MAP;
7683
});
7784
};
7885

@@ -91,22 +98,22 @@ export const useQueuedMessagesForTask = (
9198
taskId: string | undefined,
9299
): QueuedMessage[] => {
93100
return useSessionStore((s) => {
94-
if (!taskId) return [];
101+
if (!taskId) return EMPTY_QUEUE;
95102
const taskRunId = s.taskIdIndex[taskId];
96-
if (!taskRunId) return [];
103+
if (!taskRunId) return EMPTY_QUEUE;
97104
const session = s.sessions[taskRunId];
98-
return session?.messageQueue ?? [];
105+
return session?.messageQueue ?? EMPTY_QUEUE;
99106
});
100107
};
101108

102109
export const useOptimisticItemsForTask = (
103110
taskId: string | undefined,
104111
): OptimisticItem[] => {
105112
return useSessionStore((s) => {
106-
if (!taskId) return [];
113+
if (!taskId) return EMPTY_OPTIMISTIC;
107114
const taskRunId = s.taskIdIndex[taskId];
108-
if (!taskRunId) return [];
109-
return s.sessions[taskRunId]?.optimisticItems ?? [];
115+
if (!taskRunId) return EMPTY_OPTIMISTIC;
116+
return s.sessions[taskRunId]?.optimisticItems ?? EMPTY_OPTIMISTIC;
110117
});
111118
};
112119

0 commit comments

Comments
 (0)