orca/docs/reference/react-performance-audit.md

20 KiB

React Performance Audit

Status: in progress Started: 2026-05-29 Base commit: b7fe967780 (origin/main, release v1.4.35)

Goal

Scan the full React surface for $perf and $react-useeffect issues, keep coverage evidence here, and land the fixes as many small PRs with explicit merge-risk notes.

This document is the audit ledger. A section is not considered fully scanned until it has:

  1. File inventory checked from the current worktree.
  2. useEffect / useLayoutEffect / useInsertionEffect sites classified.
  3. Perf-sensitive patterns checked: timers, subscriptions, observers, JSON.stringify, storage writes, polling, broad store subscriptions, large list rendering, and render-time derived state.
  4. Each suspicious site dispositioned as "no change", "needs PR", or "covered by PR".
  5. Merge risk recorded for every PR candidate.

Scope

React hook call scan uses all repo *.ts and *.tsx files, then narrows to actual AST hook calls. The React Effect surface found on 2026-05-29 is:

  • src/renderer/src/**
  • mobile/app/**
  • mobile/src/**
  • mobile/packages/expo-two-way-audio/src/hooks.ts

Comment-only mentions outside those paths were ignored.

Initial inventory:

Metric Count
Repo *.ts / *.tsx files 3,011
Files with real Effect hook calls 285
Effect hook call sites 970
useLayoutEffect / useInsertionEffect call sites 44
Empty dependency arrays 111
Effects with cleanup returns 472
Effects with subscription/listener/observer shape 170
Effects with timer or animation-frame shape 128
Effects with JSON.stringify shape 2
Set-state-shaped Effects needing manual review 270
useMemo call sites in React scope 633
useCallback call sites in React scope 1,415
useSyncExternalStore call sites in React scope 8

Coverage Ledger

Current count after low-risk PRs #3038, #3041, #3042, #3044, #3051, #3052, #3053, #3054, #3055, #3056, #3058, #3059, and #3060: 950 Effect hook call sites.

Area Files / signal Scan status Notes
Renderer app shell src/renderer/src/App.tsx, root components Inventory complete, manual review pending Check global listeners, beforeunload, media-query, sidebar resize, active-tab repair.
Terminal / PTY components/Terminal.tsx, components/terminal-pane/**, components/terminal/** Inventory complete, manual review pending High-risk area: xterm lifecycle, scrollback, remote/mobile parity, focus, WebGL, resize.
Browser pane components/browser-pane/** Inventory complete, manual review in progress Highest Effect density: 62 sites in BrowserPane.tsx. Check driver sync, address bar derived state, find state, webview lifetime.
Editor / markdown / Monaco components/editor/** Inventory complete, manual review in progress 117 area Effects. Check editor model cleanup, preview scroll restore, search debounce, generated decorations.
Sidebar / worktrees components/sidebar/** Inventory complete, manual review in progress Check worktree list state repair, drag/drop global listeners, kanban pointer flows, inline rename.
Right sidebar / source control components/right-sidebar/** Inventory complete, manual review pending Check polling, PR checks, file explorer watch/reveal, source-control local resets. Git provider compatibility required.
Settings components/settings/** Inventory complete, manual review pending 81 area Effects. Many draft-mirror candidates; keep SSH and cross-platform settings behavior intact.
Issue, PR, task pages TaskPage.tsx, PullRequestPage.tsx, GitHubItemDialog.tsx, GitLabItemDialog.tsx, Linear components Inventory complete, manual review in progress Large files with many Effects. Separate GitHub, GitLab, Linear, and generic review behavior.
Onboarding / feature wall components/onboarding/**, components/feature-wall/** Inventory complete, manual review pending Lower merge risk for pure visual/demo state, but avoid changing telemetry semantics.
Status, dashboard, activity components/status-bar/**, components/dashboard/**, components/activity/** Inventory complete, manual review in progress Status-bar account menu close reset covered by #3051. Check interval sharing, retained agent state, activity terminal portals.
Mobile app routes mobile/app/** Inventory complete, manual review pending 79 Effects, including large tasks.tsx and session route. Remote-client parity required.
Mobile shared source mobile/src/** Inventory complete, manual review pending Browser pane, transport client context, dictation hook, bottom drawer, new worktree modal.
Expo two-way audio hook mobile/packages/expo-two-way-audio/src/hooks.ts Inventory complete, manual review pending Single Effect plus useSyncExternalStore; verify native subscription cleanup.
Tests with hook mocks *.test.ts, *.test.tsx, e2e comments Inventory complete, manual review pending Do not count comment-only mentions as app Effect sites; update tests beside behavior changes.

Current Findings Queue

These are candidate batches, not final conclusions. Each item needs code inspection before implementation.

Candidate PR Area Symptom / wasted work to prove Likely files Merge risk
PR A Small controlled-input resets Extra render pass from Effects that mirror one prop into local draft state. BrowserAddressBar.tsx covered by #3038; continue with BrowserFind.tsx, PdfFind.tsx, selected settings draft fields Low to medium
PR B Settings draft hydration Multiple independent Effects mirror persisted settings into draft state. components/settings/** Medium
PR C Browser pane Effect cluster Large Effect cluster may mix external webview sync, derived UI state, and event-specific state repair. BrowserPane.tsx, useGrabMode.ts, browser tabs High
PR D Terminal tab repair and lifecycle Effects repair active terminal/browser tab state after render; could cause extra render and focus churn. Terminal.tsx, useTerminalTabs.ts, TerminalPane.tsx High
PR E Right-sidebar polling and source-control state repair PR checks, branch snapshots, file explorer watch/reveal may repeat work after unrelated state changes. ChecksPanel.tsx, SourceControl.tsx, useGitStatusPolling.ts, file explorer hooks High
PR F Mobile route monoliths Mobile task/session routes have many chained Effects and timer/refetch patterns. mobile/app/h/[hostId]/tasks.tsx, mobile/app/h/[hostId]/session/[worktreeId].tsx High
PR G Mobile shared components Visible/open Effects reset modal/drawer state and browser address state. mobile/src/components/**, mobile/src/browser/MobileBrowserPane.tsx Medium
PR H Editor search/preview/decorations Debounce and scroll-restore Effects may be legitimate external sync but need cleanup/count review. components/editor/** Medium to high
PR I Feature wall/onboarding demos Animation/demo Effects can often move to event handlers or tighter custom hooks. components/feature-wall/**, components/onboarding/** Low to medium
PR J GitHub filter controls Extra render pass from mirroring parsed reviewer qualifier into local mode state. PRFilterDropdowns.tsx covered by #3041 Low
PR K Sidebar project filter Extra render pass from mirroring the first filtered repo into command selection state. SidebarFilter.tsx covered by #3042 Low
PR L Diff note edit draft Extra render pass from mirroring saved note body into edit draft while not editing. DiffCommentCard.tsx covered by #3044 Low
PR M Status-bar account menus Extra render pass from closing Claude/Codex account submenus in Effects after the provider menu closes. StatusBar.tsx covered by #3051 Low
PR N Browser tab favicon fallback Extra render pass from resetting failed favicon state in an Effect after tab favicon identity changes. BrowserTab.tsx covered by #3052 Low
PR O Workspace title rename draft Redundant draft sync Effect runs while the inline title rename input is not mounted. WorktreeTitleInlineRename.tsx covered by #3053 Low
PR P Feature-wall tour static substep repair Three no-op Effects scan static step arrays and repair ids that are only written from those arrays. FeatureWallTourSurface.tsx covered by #3054 Low
PR Q Repo combobox mount-open state Mount-only auto-open path uses an Effect and ref guard instead of initializing state from the prop. RepoCombobox.tsx covered by #3055 Low
PR R Quick Open query reset Extra render pass from clearing the Quick Open input after the dialog opens. QuickOpen.tsx covered by #3056 Low
PR S Project group dialog open resets Extra render pass from seeding name/delete dialog local state after the dialog opens. ProjectGroupNameDialog.tsx, ProjectGroupDeleteDialog.tsx covered by #3058 Low
PR T Onboarding agent fallback disclosure Extra render pass from opening the fallback agent list when a selected agent first appears there. AgentStep.tsx covered by #3059 Low
PR U Feature-wall tour workflow resets Four local reset Effects run after workflow changes or close instead of in the selection/close path. FeatureWallTourSurface.tsx covered by #3060 Low

Merge Risk Scale

Risk Criteria Required verification
Low Isolated local UI state, no persistence, no IPC, no terminal/browser/source-control behavior. Targeted unit test if available plus pnpm run typecheck:web.
Medium Settings, editor UI, mobile local UI, or state persisted per workspace but no transport protocol changes. Targeted tests plus manual interaction or focused Playwright/Electron check when UI behavior changes.
High Terminal/PTY, browser webview, source control, mobile remote-client, SSH, polling, persistence, or provider behavior. Targeted tests, pnpm run typecheck:web, and Electron or mobile/manual parity evidence as appropriate.

PR Log

PR Branch Area Risk Status Evidence
#3038 nwparker/react-perf Audit ledger plus browser address bar top-suggestion mirror Effect removal Low Merged pnpm exec oxlint src/renderer/src/components/browser-pane/BrowserAddressBar.tsx; pnpm run typecheck:web.
#3041 nwparker/react-perf-pr-filter PR reviewer filter mode derived from parsed query plus explicit user override Low Merged pnpm exec oxlint src/renderer/src/components/github/PRFilterDropdowns.tsx; pnpm run typecheck:web.
#3042 nwparker/react-perf-sidebar-filter Sidebar project filter command selection derived from filtered repos Low Merged pnpm exec oxlint src/renderer/src/components/sidebar/SidebarFilter.tsx; pnpm run typecheck:web.
#3044 nwparker/react-perf-low-risk-2 Diff note card removes saved-body-to-draft mirror Effect Low Merged pnpm exec oxlint src/renderer/src/components/diff-comments/DiffCommentCard.tsx; pnpm run typecheck:web.
#3051 nwparker/react-perf-low-risk-3 Status-bar account submenus collapse in provider menu open-change handlers Low Merged pnpm exec oxlint src/renderer/src/components/status-bar/StatusBar.tsx; pnpm run typecheck:web.
#3052 nwparker/react-perf-low-risk-4 Browser tab favicon failure reset happens during render for new favicon IDs Low Merged pnpm exec oxlint src/renderer/src/components/tab-bar/BrowserTab.tsx; pnpm run typecheck:web.
#3053 nwparker/react-perf-low-risk-5 Inline workspace-title rename removes inactive draft sync Effect Low Merged pnpm exec oxlint src/renderer/src/components/sidebar/WorktreeTitleInlineRename.tsx; pnpm run typecheck:web.
#3054 nwparker/react-perf-low-risk-6 Feature-wall tour removes static substep id repair Effects Low Merged pnpm exec oxlint src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx; pnpm run typecheck:web.
#3055 nwparker/react-perf-low-risk-7 Repo combobox initializes mount-open state without an Effect Low Merged pnpm exec oxlint src/renderer/src/components/repo/RepoCombobox.tsx; pnpm run typecheck:web.
#3056 nwparker/react-perf-low-risk-8 Quick Open clears its query on the open edge without a reset Effect Low Merged pnpm exec oxlint src/renderer/src/components/QuickOpen.tsx; pnpm run typecheck:web.
#3058 nwparker/react-perf-project-group-dialogs Project group dialogs reset local open-state during render Low Merged pnpm exec oxlint src/renderer/src/components/sidebar/ProjectGroupDeleteDialog.tsx src/renderer/src/components/sidebar/ProjectGroupNameDialog.tsx; pnpm run typecheck:web.
#3059 nwparker/react-perf-agent-step-latch Onboarding agent fallback disclosure latch updates during render Low Merged pnpm exec oxlint src/renderer/src/components/onboarding/AgentStep.tsx; pnpm run typecheck:web.
#3060 nwparker/react-perf-feature-tour-resets Feature-wall tour reset state moves into workflow/close transitions Low Open pnpm exec oxlint src/renderer/src/components/feature-wall/FeatureWallTourSurface.tsx; pnpm run typecheck:web.

Reproduction Commands

Run from the worktree root.

rg --files -g '*.tsx' -g '*.ts' | wc -l
rg -l "\\b(useEffect|useLayoutEffect|React\\.useEffect|React\\.useLayoutEffect)\\b" -g '*.tsx' -g '*.ts' | sort

The exact Effect-site counts above came from a TypeScript AST scan so comment-only mentions are not counted.