Skip to content

[Due for payment 2026-03-30] FloatingActionButtonAndPopover is a ~770-line monolith that violates single-responsibility and causes unnecessary re-renders #84874

@mountiny

Description

@mountiny

Problem

FloatingActionButtonAndPopover.tsx has grown into a ~770-line monolithic component that owns too many responsibilities:

  • 22 useOnyx subscriptions (session, policies, reports, drafts, violations, betas, account, travel, onboarding, etc.)
  • 12 useMemo and 11 useCallback hooks for manual memoization
  • Menu item construction (~200 lines building a single menuItems array)
  • Quick action state, avatars, title/subtitle derivation
  • Permission checks (invoice, travel, delegate access, billing grace period)
  • Modal orchestration (redirect-to-Classic, empty report confirmation)
  • Keyboard navigation wiring
  • Layout rendering (FAB button, receipt button, PopoverMenu with positioning)
  • Telemetry span management

This causes several concrete problems:

  1. Cascading re-renders: Any single Onyx key change (e.g. a policy update, a new transaction draft, a beta flag flip) triggers a re-render of the entire component, re-evaluating all 22 subscriptions and rebuilding all 12 memoized values -- even when only one menu item cares about the changed data.
  2. Poor maintainability: Adding or modifying a single menu item (e.g. adding a new FAB action) requires understanding ~770 lines of interleaved concerns. The component mixes data fetching, visibility logic, navigation, modals, and rendering in a single function body.
  3. Manual memoization burden: The 12 useMemo + 11 useCallback calls exist solely to mitigate the re-render problem caused by co-locating unrelated data in one component. This is the opposite of our React Compiler strategy, which works best when components are small and focused.
  4. Testing difficulty: The component cannot be unit-tested in isolation per feature -- testing invoice menu item visibility requires mounting the entire FAB with all 22 Onyx subscriptions mocked.

Proposal

Decompose into a composable architecture where:

  • Each FAB menu item (Expense, Invoice, Travel, Quick Action, etc.) becomes a self-contained component that fetches its own data, owns its visibility logic, and registers itself with a lightweight context for keyboard navigation
  • The parent component shrinks to ~80 lines of pure composition
  • Manual memoization (useCallback, useMemo) is removed in favor of React Compiler
  • A FABMenuContext provides keyboard navigation registration without prop drilling

This follows the Clean React coding standards and aligns with our React Compiler adoption strategy.

Issue OwnerCurrent Issue Owner: @mallenexpensify

Metadata

Metadata

Labels

Awaiting PaymentAuto-added when associated PR is deployed to productionDailyKSv2ImprovementItem broken or needs improvement.OverdueTask

Type

No type
No fields configured for issues without a type.

Projects

Status

No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions