perf(web): reuse sessions reference to stop sidebar re-render on streaming deltas (#1113)

This commit is contained in:
qer 2026-06-26 02:24:31 +08:00 committed by GitHub
parent 6a97d0bf43
commit 6194d3fad3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 56 additions and 1 deletions

View file

@ -0,0 +1,8 @@
---
"@moonshot-ai/kimi-code": patch
---
Keep the web session sidebar from re-rendering on every streaming token. The
event reducer now reuses the `sessions` array reference for events that do not
change sessions, so the sidebar computeds (`sessionsForView` / `workspaceGroups`
/ `mergedWorkspaces`) are no longer dirtied by unrelated high-frequency events.

View file

@ -84,7 +84,13 @@ export function createInitialState(): KimiClientState {
function cloneState(s: KimiClientState): KimiClientState {
return {
...s,
sessions: [...s.sessions],
// Reuse the `sessions` array reference when an event does not touch it.
// Every session-mutating case below already builds its own array via
// `[...]` / `.map` / `.filter`, so sharing the reference is safe — and it
// keeps `rawState.sessions` stable for events that don't change sessions,
// so the sidebar computeds (sessionsForView / workspaceGroups /
// mergedWorkspaces) are not dirtied by unrelated events.
sessions: s.sessions,
messagesBySession: { ...s.messagesBySession },
approvalsBySession: { ...s.approvalsBySession },
planReviewByToolCallId: { ...s.planReviewByToolCallId },

View file

@ -149,3 +149,44 @@ describe('reduceAppEvent taskProgress', () => {
expect(lines?.at(-1)).toBe('line 59');
});
});
describe('reduceAppEvent sessions reference stability', () => {
// The sidebar computeds (sessionsForView / workspaceGroups / mergedWorkspaces)
// depend on `rawState.sessions`. Events that do not change sessions must keep
// the SAME array reference so those computeds are not dirtied; events that do
// change sessions must produce a NEW array.
it('reuses the sessions reference for an event that does not touch sessions', () => {
const state = {
...createInitialState(),
sessions: [makeSession('s1', '2026-01-01T00:00:00.000Z')],
messagesBySession: { s1: [makeMessage('s1', '2026-01-01T00:00:00.000Z')] },
};
const next = reduceAppEvent(
state,
{
type: 'messageUpdated',
sessionId: 's1',
messageId: 'msg_2026-01-01T00:00:00.000Z',
content: [{ type: 'text', text: 'updated' }],
status: 'completed',
},
{ sessionId: 's1', seq: 2 },
);
expect(next.sessions).toBe(state.sessions);
});
it('produces a new sessions array for an event that changes sessions', () => {
const state = {
...createInitialState(),
sessions: [makeSession('s1', '2026-01-01T00:00:00.000Z')],
};
const next = reduceAppEvent(
state,
{ type: 'sessionCreated', session: makeSession('s2', '2026-02-01T00:00:00.000Z') },
{ sessionId: 's2', seq: 3 },
);
expect(next.sessions).not.toBe(state.sessions);
expect(next.sessions.map((s) => s.id)).toEqual(['s2', 's1']);
});
});