Skip to content

[P1][arch] StatusBar re-renders on every tabs change — wide selector #458

Description

@EVWorth

Context

StatusBar reads the entire tabs array and activeTabId from useEditorStore. Both reads can be narrowed to selectors.

Problem

src/components/layout/StatusBar.tsx:56-58:

const tabs = useEditorStore((s) => s.tabs);
const activeTabId = useEditorStore((s) => s.activeTabId);

The two reads are used for:

  • dirtyTabs = tabs.filter(t => t.isDirty) and hasDirtyTabs = dirtyTabs.length > 0 (lines 96-97) — used only by the update-available button's dirty-tab guard.
  • activeTab = tabs.find(t => t.id === activeTabId) (line 105) — used only for selectedDatabase = activeTab?.database ?? activeConn?.database (line 106).

Both depend on a small subset of the tab state. Yet every updateTabContent keystroke writes tabs: state.tabs.map(...) (src/stores/editorStore.ts:313-316) and triggers a StatusBar re-render. With Monaco auto-save debounce at 500ms (editorStore.ts:355-365), this isn't a per-keystroke storm, but every editor action (rename, reorder, setDirty, setConnection) re-renders StatusBar. The 404-LOC component re-rendering on tab churn makes its own update-overlay reflow visible.

The cross-cutting audit (docs/audits/cross-cutting.md F13) flagged this as P1.

Files

  • src/components/layout/StatusBar.tsx:56-58 — wide selectors
  • src/stores/editorStore.ts:313-316 — updateTabContent triggers map and re-render

Repro

// In a test, mount StatusBar, count renders.
useEditorStore.setState({ tabs: [...prevTabs, newTab] }); // unrelated new tab
// StatusBar re-renders even if activeTab.database didn't change.

Expected

StatusBar only re-renders when:

  • hasDirtyTabs changes.
  • activeTab.database changes.
  • The result/error state changes (already uses narrow selectors).

Proposed fix

Scope S. Use narrow selectors:

const hasDirtyTabs = useEditorStore((s) => s.tabs.some((t) => t.isDirty));
const selectedDatabase = useEditorStore((s) => {
  const tab = s.tabs.find((t) => t.id === s.activeTabId);
  return tab?.database;
});

Or split into <DirtyTabIndicator /> and <SelectedDatabaseIndicator /> sub-components (per #451 / F6) so each subscribes narrowly.

Acceptance

A test using useEditorStore.setState({ tabs: [...] }) asserts StatusBar does not re-render if no tab's isDirty changed and the active tab's database didn't change.

Needs human verify

No (selector refactor).

Labels: audit, area/cross-cutting, severity/p1, kind/arch

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/cross-cuttingAudit areaauditTracks a feature-by-feature codebase audit findingkind/archAudit finding categoryseverity/p1Audit finding severity

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions