Skip to content

perf: Replace Array.includes() with Set, use toSorted(), and optimize iterations #231

@Shironex

Description

@Shironex

Summary

A codebase review identified JavaScript performance patterns that can be improved. The most impactful are Array.includes() calls inside loops (O(n²) instead of O(n)), mutating .sort() where .toSorted() is preferred for immutability, and multiple array iterations that could be combined.

Note: React version is 18.3.1 — React 19 API findings (forwardRef, use()) are listed as future-proofing items for when the upgrade happens.

Findings

1. Array.includes() inside loops — should use Set — MEDIUM

File Line Pattern Impact
App.tsx 82 allIds.filter(id => !currentOrder.includes(id)) Runs on every session change. Line 80 already creates allIdSet — apply same pattern to currentOrder
stores/useWorkspaceStore.ts 363 sessionIds.filter(id => validSessionIds.includes(id)) inside .map() O(tabs × sessionIds × validSessionIds). Called during rehydration
components/shared/UsagePopover.tsx 143 sessions.filter(s => activeTab.sessionIds.includes(s.id)) Runs inside useMemo on every session/tab change
lib/plugin-theme-injector.ts 136 (ALL_THEMES as string[]).includes(themeId) 40+ element array. Easy fix — wrap in a static Set at module level

Fix pattern:

const validSet = new Set(validSessionIds);
const filtered = sessionIds.filter(id => validSet.has(id));

2. Mutating .sort() — prefer .toSorted() — LOW

All instances operate on freshly-created local arrays so they're safe in practice, but .toSorted() is more defensive and idiomatic.

File Line Context
components/settings/SettingsNavigation.tsx 108 catSections.sort(...)
components/settings/SettingsNavigation.tsx 127 pluginGroups.sort(...)
components/plugin/ExtensionSlot.tsx 75 matches.sort(...)
components/plugin/ExtensionSlot.tsx 94 matches.sort(...)
components/shared/UsagePopover.tsx 169 panels.sort(...)

Note: Verify tsconfig.json lib includes ES2023 before using .toSorted().

3. Multiple array iterations — LOW

File Lines Pattern
components/shared/BranchSelector.tsx 54-62 Triple iteration: filter() + two more filter()s to partition local/remote. Also calls searchQuery.toLowerCase() per branch instead of once
components/shared/BranchAutocomplete.tsx 64-72 Double filter() outside useMemo — runs on every render even when data hasn't changed

Fix: Combine into single loop that partitions into local/remote:

const { local, remote } = useMemo(() => {
  const lower = search.toLowerCase();
  const local: Branch[] = [], remote: Branch[] = [];
  for (const b of branches) {
    if (search && !b.name.toLowerCase().includes(lower)) continue;
    (b.isRemote ? remote : local).push(b);
  }
  return { local, remote };
}, [branches, search]);

4. Early exit optimization — LOW

File: components/terminal/SessionStatusDisplay.tsx (lines 26-49)

getModeConfig() does [...statusRenderers.values()].find() and providers.find() before checking if (aiMode === 'plain') — those results are discarded for the 'plain' case. Move the 'plain' check to the top.

5. React 19 future-proofing (no action needed now)

forwardRef usage: 14 files with ~35 forwardRef calls, mostly in components/ui/ (shadcn/ui). When upgrading to React 19, these can use ref as a regular prop. Wait for shadcn to ship React 19-compatible templates.

useContext usage: Zero instances found — the codebase uses Zustand exclusively. No migration needed.

Checklist

Quick wins:

  • Convert currentOrder to Set in App.tsx:82
  • Convert validSessionIds to Set in useWorkspaceStore.ts:357-368
  • Convert activeTab.sessionIds to Set in UsagePopover.tsx:143
  • Create static Set from ALL_THEMES in plugin-theme-injector.ts
  • Move 'plain' early return before find() calls in SessionStatusDisplay.tsx

Low priority:

  • Replace .sort() with .toSorted() in 5 locations (verify ES2023 support first)
  • Combine filter+partition in BranchSelector.tsx and BranchAutocomplete.tsx into single loop inside useMemo
  • Track React 19 upgrade for forwardRef removal (blocked on shadcn/ui compatibility)

Verification

  1. pnpm typecheck and pnpm test
  2. For Set conversions: improvements are algorithmic, no benchmarking needed at current scale
  3. For .toSorted(): verify tsconfig.json has "lib": ["ES2023"] or similar

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2-mediumFix soonarea:frontendReact UI, stores, componentsperformancePerformance improvements and optimizations

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions