perf(site/AgentsSidebar): memoize chat tree derivation#23694
Draft
perf(site/AgentsSidebar): memoize chat tree derivation#23694
Conversation
The React Compiler left buildChatTree, new Map, and collectVisibleChatIDs unguarded in AgentsSidebar (94 cache slots, too complex for the compiler to optimize). This made chatTree, chatById, and visibleChatIDs new objects every render, forcing every ChatTreeNode context consumer to re-render. Extract the derivation chain into useDerivedChatTree with useMemo keyed on chats. The compiler now: - Guards all derivation on chats (12 cache slots in the hook) - Guards the ChatTreeContext value (was unguarded, now 12 deps) - Guards the chat list JSX (was unguarded, now 12 deps) AgentsSidebar grows from 94 to 118 cache slots because the compiler can now memoize more downstream expressions.
Contributor
DanielleMaywood
left a comment
There was a problem hiding this comment.
If much prefer this wasn't a custom hook. If we need the useMemo, just do it inline
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The React Compiler left
buildChatTree,new Map, andcollectVisibleChatIDsunguarded inAgentsSidebar(94 cache slots). This made every derived value a new object on every render, forcing everyChatTreeNodecontext consumer to re-render regardless of whetherchatschanged.Root cause
When
AgentsSidebarcompiles to 94 cache slots, the compiler chooses not to guard certain computation chains. Minimal reproductions with the same hook/computation structure ARE guarded; the full component's complexity exceeds the compiler's optimization budget. The result:chatTree,chatById,visibleChatIDsare new references on every render, theChatTreeContextvalue is always new, and NChatTreeNodeinstances re-render on every sidebar render (navigation, status update, etc.).Fix
Extract the derivation chain into
useDerivedChatTree(chats)withuseMemo. The compiler now:_c(12)), guarding all derivation onchatsChatTreeContextvalue inAgentsSidebar(was unguarded, now 12 deps)AgentsSidebargrows from 94 to 118 cache slots (more expressions are now memoizable)Performance results
Chrome DevTools Performance recording, 3 sidebar navigation clicks:
Proof
Compiled output: Before: 0 guards on 5 derivation statements. After: all 5 inside
if ($[0] !== chats)in the hook. Context value creation, previously unguarded, is now behind a 12-dep guard.Browser profiling: 26.5% scripting reduction on sidebar navigation interactions.
vitest:
useDerivedChatTreereference stability tests. Samechatsref → same return object (toBe). Differentchatsref → new data. 35 tests pass (33 existing + 2 new).