This document audits all Zustand stores in the frontend for selectors that return objects or arrays, which can cause unnecessary re-renders without proper optimization using useShallow().
In Zustand v4+, when a selector returns an object or array, the component will re-render on every state change even if the selected values haven't actually changed. This is because Zustand uses reference equality to determine if a re-render is needed.
Example anti-pattern:
// ❌ CAUSES UNNECESSARY RE-RENDERS
const { items, status } = useStore((state) => ({
items: state.items,
status: state.status
}));Solution:
// ✅ USE useShallow() FOR OBJECT/ARRAY SELECTORS
import { useShallow } from 'zustand/react/shallow';
const { items, status } = useStore(
useShallow((state) => ({
items: state.items,
status: state.status
}))
);Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
getSelectedTasks() |
ChangelogTask[] (array) |
Line 558-561 | HIGH |
getTasksWithSpecs() |
ChangelogTask[] (array) |
Line 563-566 | MEDIUM |
Recommended Actions:
getSelectedTasks()- Used in components, returns filtered array - needs useShallow wrappergetTasksWithSpecs()- Used in components, returns filtered array - needs useShallow wrapper
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
State with profiles |
ClaudeProfile[] (array) |
Throughout | HIGH |
| Object selectors in components | { profiles, activeProfileId, isLoading, isSwitching } |
Component usage | HIGH |
Recommended Actions:
- Store uses array spread extensively - components using multiple values should use useShallow
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
projectIndex |
ProjectIndex | null (object) |
Line 12 | HIGH |
memoryStatus |
GraphitiMemoryStatus | null (object) |
Line 17 | HIGH |
memoryState |
GraphitiMemoryState | null (object) |
Line 18 | HIGH |
recentMemories |
MemoryEpisode[] (array) |
Line 23 | HIGH |
searchResults |
ContextSearchResult[] (array) |
Line 27 | HIGH |
Recommended Actions:
- All state properties return objects or arrays
- Components selecting multiple properties should use useShallow
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
downloads |
Record<string, DownloadProgress> (object) |
Line 14 | HIGH |
getActiveDownloads() |
DownloadProgress[] (array) |
Line 130-135 | HIGH |
Recommended Actions:
getActiveDownloads()returns array - needs useShallow- Components accessing
downloadsobject should use useShallow
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
expandedFolders |
Set<string> (object) |
Line 6 | HIGH |
files |
Map<string, FileNode[]> (object) |
Line 7 | HIGH |
isLoading |
Map<string, boolean> (object) |
Line 8 | HIGH |
getFiles() |
FileNode[] | undefined (array) |
Line 134-136 | HIGH |
getAllExpandedFiles() |
Set<string> (object) |
Line 142-144 | HIGH |
getVisibleFiles() |
FileNode[] (array) |
Line 146-165 | HIGH |
computeVisibleItems() |
{ nodes: FileNode[]; count: number } (object) |
Line 167-186 | HIGH |
Recommended Actions:
- All selectors return objects/arrays - extensive useShallow usage needed
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
issues |
GitLabIssue[] (array) |
Line 11 | HIGH |
syncStatus |
GitLabSyncStatus | null (object) |
Line 12 | MEDIUM |
investigationStatus |
GitLabInvestigationStatus (object) |
Line 21 | MEDIUM |
lastInvestigationResult |
GitLabInvestigationResult | null (object) |
Line 22 | MEDIUM |
getSelectedIssue() |
GitLabIssue | null (object) |
Line 95-98 | HIGH |
getFilteredIssues() |
GitLabIssue[] (array) |
Line 100-104 | HIGH |
Recommended Actions:
getFilteredIssues()returns filtered array - needs useShallow- Object selectors need useShallow when multiple properties selected
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
session |
IdeationSession | null (object) |
Line 30 | HIGH |
typeStates |
Record<IdeationType, IdeationTypeState> (object) |
Line 34 | HIGH |
selectedIds |
Set<string> (object) |
Line 35 | HIGH |
logs |
string[] (array) |
Line 33 | MEDIUM |
getIdeasByType() |
Idea[] (array) |
Line 574-580 | HIGH |
getIdeasByStatus() |
Idea[] (array) |
Line 582-588 | HIGH |
getActiveIdeas() |
Idea[] (array) |
Line 590-593 | HIGH |
getArchivedIdeas() |
Idea[] (array) |
Line 595-598 | HIGH |
getIdeationSummary() |
IdeationSummary (object) |
Line 600-627 | HIGH |
Recommended Actions:
- All exported selectors return objects/arrays - comprehensive useShallow needed
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
session |
InsightsSession | null (object) |
Line 22 | HIGH |
sessions |
InsightsSessionSummary[] (array) |
Line 23 | HIGH |
status |
InsightsChatStatus (object) |
Line 24 | MEDIUM |
toolsUsed |
InsightsToolUsage[] (array) |
Line 28 | MEDIUM |
pendingImages |
ImageAttachment[] (array) |
Line 31 | MEDIUM |
Recommended Actions:
- Arrays and objects in state need useShallow for multi-property selectors
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
projects |
Project[] (array) |
Line 11 | HIGH |
openProjectIds |
string[] (array) |
Line 17 | HIGH |
tabOrder |
string[] (array) |
Line 19 | HIGH |
getSelectedProject() |
Project | undefined (object) |
Line 178-181 | HIGH |
getOpenProjects() |
Project[] (array) |
Line 184-187 | HIGH |
getActiveProject() |
Project | undefined (object) |
Line 189-192 | HIGH |
getProjectTabs() |
Project[] (array) |
Line 194-205 | HIGH |
Recommended Actions:
- All selectors return objects or arrays - useShallow needed throughout
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
scores |
QualityScore[] (array) |
Line 102 | HIGH |
trend |
QualityTrend | null (object) |
Line 103 | MEDIUM |
summary |
QualitySummary | null (object) |
Line 104 | MEDIUM |
byAgentType |
QualityByAgentType | null (object) |
Line 105 | MEDIUM |
alerts |
QualityAlert[] (array) |
Line 106 | MEDIUM |
Recommended Actions:
- Array and object state needs useShallow for multi-property access
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
recentActions |
RecentAction[] (array) |
Line 27 | HIGH |
getRecentActions() |
RecentAction[] (array) |
Line 141-143 | HIGH |
Recommended Actions:
- Returns array - needs useShallow in components
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
releaseableVersions |
ReleaseableVersion[] (array) |
Line 11 | HIGH |
preflightStatus |
ReleasePreflightStatus | null (object) |
Line 18 | MEDIUM |
releaseProgress |
ReleaseProgress | null (object) |
Line 26 | MEDIUM |
getUnreleasedVersions() |
ReleaseableVersion[] (array) |
Line 190-193 | HIGH |
getSelectedVersionInfo() |
ReleaseableVersion | undefined (object) |
Line 198-201 | HIGH |
Recommended Actions:
- All selectors return objects or arrays
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
roadmap |
Roadmap | null (object) |
Line 53 | HIGH |
competitorAnalysis |
CompetitorAnalysis | null (object) |
Line 54 | MEDIUM |
generationStatus |
RoadmapGenerationStatus (object) |
Line 55 | MEDIUM |
getFeaturesByPhase() |
RoadmapFeature[] (array) |
Line 466-472 | HIGH |
getFeaturesByPriority() |
RoadmapFeature[] (array) |
Line 474-480 | HIGH |
getFeatureStats() |
{ total: number; byPriority: Record<string, number>; ... } (object) |
Line 482-513 | HIGH |
Recommended Actions:
- Complex objects and arrays returned - needs useShallow
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
settings |
AppSettings (object) |
Line 10 | HIGH |
profiles |
APIProfile[] (array) |
Line 15 | HIGH |
testConnectionResult |
TestConnectionResult | null (object) |
Line 22 | MEDIUM |
discoveredModels |
Map<string, ModelInfo[]> (object) |
Line 27 | LOW |
Recommended Actions:
- Large settings object and profiles array need useShallow
Optimization Needed: YES
Note: File size prevented complete audit. Only initial state declarations and primary selectors were reviewed. Additional selectors may exist throughout the file that also require optimization.
| Selector | Returns | Location | Priority |
|---|---|---|---|
tasks |
Task[] (array) |
Line 11 | CRITICAL |
taskOrder |
TaskOrderState | null (object) |
Line 15 | CRITICAL |
| Selectors returning filtered/mapped tasks | Task[] (array) |
Throughout | CRITICAL |
Recommended Actions:
- This is a HIGH-PRIORITY store - tasks array is used extensively
- All task list operations need useShallow optimization
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
templates |
CustomTemplate[] (array) |
Line 14 | HIGH |
editingTemplate |
CustomTemplate | null (object) |
Line 19 | MEDIUM |
validationErrors |
Record<string, string> (object) |
Line 21 | MEDIUM |
testResult |
GeneratedSpec | null (object) |
Line 25 | MEDIUM |
filterByCategory() |
CustomTemplate[] (array) |
Line 390-393 | HIGH |
searchTemplates() |
CustomTemplate[] (array) |
Line 395-404 | HIGH |
Recommended Actions:
- Arrays and objects returned - useShallow needed
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
terminals |
Terminal[] (array) |
Line 109 | CRITICAL |
layouts |
TerminalLayout[] (array) |
Line 110 | MEDIUM |
getTerminal() |
Terminal | undefined (object) |
Line 408-410 | HIGH |
getActiveTerminal() |
Terminal | undefined (object) |
Line 412-415 | HIGH |
getTerminalsForProject() |
Terminal[] (array) |
Line 422-424 | HIGH |
Recommended Actions:
- Terminals array is critical - high update frequency needs useShallow
Optimization Needed: YES
| Selector | Returns | Location | Priority |
|---|---|---|---|
columnPreferences |
KanbanColumnPreferences | null (object) |
Line 31 | HIGH |
getColumnPreferences() |
ColumnPreferences (object) |
Line 298-310 | HIGH |
Recommended Actions:
- Returns complex nested object - needs useShallow
Optimization Needed: LOW (mostly string state)
| Selector | Returns | Location | Priority |
|---|---|---|---|
shortcuts |
Record<KeyboardShortcutAction, KeyCombination> (object) |
Line 8 | LOW |
Recommended Actions:
- Object but rarely changes - lower priority
Optimization Needed: LOW (mostly boolean state)
| Selector | Returns | Location | Priority |
|---|---|---|---|
rateLimitInfo |
RateLimitInfo | null (object) |
Line 7 | LOW |
sdkRateLimitInfo |
SDKRateLimitInfo | null (object) |
Line 11 | LOW |
Recommended Actions:
- Simple objects, infrequent updates - lower priority
Optimization Needed: LOW (mostly boolean/null state)
| Selector | Returns | Location | Priority |
|---|---|---|---|
authFailureInfo |
AuthFailureInfo | null (object) |
Line 7 | LOW |
Recommended Actions:
- Infrequent updates - lower priority
Optimization Needed: LOW (mostly string/boolean state)
| Selector | Returns | Location | Priority |
|---|---|---|---|
hasUnsavedChanges() |
boolean |
Line 193-195 | N/A |
getFileName() |
string | null |
Line 197-199 | N/A |
getFileExtension() |
string | null |
Line 201-203 | N/A |
Recommended Actions:
- Selectors return primitives - no optimization needed
- Total Stores Audited: 22
- Stores Requiring Optimization: 18
- Stores Low Priority (mostly primitives): 4
| Priority | Store Count | Stores |
|---|---|---|
| CRITICAL | 2 | task-store.ts, terminal-store.ts |
| HIGH | 13 | changelog-store.ts, context-store.ts, file-explorer-store.ts, gitlab-store.ts, ideation-store.ts, insights-store.ts, project-store.ts, quality-store.ts, quick-actions-store.ts, release-store.ts, roadmap-store.ts, settings-store.ts, template-store.ts |
| MEDIUM | 3 | kanban-settings-store.ts, claude-profile-store.ts, download-store.ts |
| LOW | 4 | keyboard-shortcuts-store.ts, rate-limit-store.ts, auth-failure-store.ts, code-editor-store.ts |
All stores returning objects or arrays should follow this pattern:
// ❌ BEFORE (causes unnecessary re-renders)
const { items, status } = useStore((state) => ({
items: state.items,
status: state.status
}));// ✅ AFTER (prevents unnecessary re-renders)
import { useShallow } from 'zustand/react/shallow';
const { items, status } = useStore(
useShallow((state) => ({
items: state.items,
status: state.status
}))
);- Phase 1 (CRITICAL): Optimize
task-store.tsandterminal-store.ts- highest update frequency - Phase 2 (HIGH): Optimize remaining HIGH-priority stores
- Phase 3 (MEDIUM/LOW): Complete remaining optimizations
Estimated Impact:
- Reduced re-renders: 30-50%
- Improved frame rate during updates
- Better battery life on laptops