Skip to content

perf(desktop): use granular selector in WorkspaceListItem to prevent rerenders#672

Closed
Kitenite wants to merge 1 commit intomainfrom
consider-rerenders
Closed

perf(desktop): use granular selector in WorkspaceListItem to prevent rerenders#672
Kitenite wants to merge 1 commit intomainfrom
consider-rerenders

Conversation

@Kitenite
Copy link
Copy Markdown
Collaborator

@Kitenite Kitenite commented Jan 8, 2026

Summary

  • Replace full tabs and panes store subscriptions with a single granular selector in WorkspaceListItem
  • Selector computes workspace status directly and returns a primitive value
  • Prevents all workspace sidebar items from rerendering when any pane status changes

Before: Each WorkspaceListItem subscribed to entire tabs/panes objects, causing ALL workspace items to rerender when ANY pane status changed anywhere in the app.

After: Single selector filters tabs by workspace ID, extracts pane IDs, computes highest priority status, and returns a primitive. Zustand's Object.is equality check prevents rerenders unless the computed status actually changes.

Test plan

  • Open multiple workspaces in sidebar
  • Run an agent in one workspace terminal (triggers frequent status changes)
  • Verify other workspace items don't rerender (use React DevTools Profiler)
  • Verify status indicator still updates correctly for the active workspace

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Optimized workspace status detection to reduce unnecessary interface updates while maintaining existing functionality and user experience.

✏️ Tip: You can customize this high-level summary in your review settings.

…rerenders

Replace full `tabs` and `panes` store subscriptions with a single granular
selector that computes workspace status directly and returns a primitive.

Before: Each WorkspaceListItem subscribed to entire tabs/panes objects,
causing ALL workspace items to rerender when ANY pane status changed.

After: Selector filters tabs by workspace ID, extracts pane IDs, and
computes highest priority status inside the selector. Returns primitive
string/null so Zustand's Object.is equality check prevents rerenders
unless the computed status actually changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

This change refactors workspace status derivation in the WorkspaceListItem component by replacing broad tabs/panes state usage with a granular, memoized selector. A useCallback-based selector now computes workspace status by filtering tabs and aggregating pane statuses. Previous useMemo-based memoization blocks are removed. The component's public API and UI behavior remain unchanged.

Changes

Cohort / File(s) Change Summary
Workspace Status Selector Refactoring
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
Introduces granular useCallback-based selector to derive workspace status by filtering workspace tabs, collecting pane IDs from tab layouts, and computing highest priority status. Removes previous useMemo-based memoization of workspacePaneIds and workspaceStatus. Adds TabsStore type import. Reduces unnecessary re-renders by deriving status via dedicated selector.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A selector so fine, with care now we weave,
Memoized just right, no render reprieve,
Workspace status flows clean, through pane-filtered streams,
Each hop more efficient—fulfilling our dreams! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: using a granular selector in WorkspaceListItem for performance optimization to prevent unnecessary rerenders.
Description check ✅ Passed The description provides a comprehensive summary of changes, clear before/after rationale, and a detailed test plan. It covers the key sections of the template with substantive information.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2451a4 and ca8f654.

📒 Files selected for processing (1)
  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
🧰 Additional context used
📓 Path-based instructions (6)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
apps/desktop/src/renderer/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
🧠 Learnings (1)
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx
🧬 Code graph analysis (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx (4)
apps/desktop/src/renderer/stores/tabs/types.ts (1)
  • TabsStore (57-125)
apps/desktop/src/renderer/stores/tabs/utils.ts (1)
  • extractPaneIdsFromLayout (54-65)
apps/desktop/src/shared/tabs-types.ts (1)
  • getHighestPriorityStatus (54-69)
apps/desktop/src/renderer/stores/tabs/store.ts (1)
  • useTabsStore (74-924)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (1)
apps/desktop/src/renderer/screens/main/components/WorkspaceSidebar/WorkspaceListItem/WorkspaceListItem.tsx (1)

87-105: Clean implementation of the granular selector pattern.

The selector correctly:

  1. Computes derived state inside the selector (filtering tabs, extracting pane IDs, computing status)
  2. Returns a primitive (ActivePaneStatus | null) enabling Zustand's Object.is equality check to skip rerenders when the status hasn't changed
  3. Uses a generator for lazy evaluation, which pairs well with getHighestPriorityStatus's early exit on max priority

This is the idiomatic Zustand pattern for preventing unnecessary rerenders from derived state. Based on learnings, this aligns with the preference for Zustand state management.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Kitenite Kitenite closed this Jan 10, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ⚠️ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

@Kitenite Kitenite deleted the consider-rerenders branch January 13, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant