Skip to content

perf(wallet): fix React Compiler bailouts on Asset Details - #34827

Draft
juanmigdr wants to merge 1 commit into
mainfrom
perf/asset-details-render-storm
Draft

perf(wallet): fix React Compiler bailouts on Asset Details#34827
juanmigdr wants to merge 1 commit into
mainfrom
perf/asset-details-render-storm

Conversation

@juanmigdr

Copy link
Copy Markdown
Member

Description

The Asset Details screen's chart/transactions area (PriceChartProvider and its subtree) was re-rendering 6 times per visit instead of getting memoized by React Compiler. The root cause: several components in that subtree had Rules-of-React violations (reading refs during render, throw/ternaries/optional-chaining inside try/catch) that made the compiler skip optimizing them entirely.

This PR fixes those compiler bailouts across TokenDetails (both the Views and AssetOverview versions), AssetOverviewContent, TokenDetailsActions, and Transactions/index.js — mostly by moving ref writes into effects and rewriting a handful of try/catch bodies to avoid patterns the compiler can't yet handle. I verified against the actual compiler output that every single rewrite is necessary (reverting any one of them reintroduces its specific bailout), so there's no unnecessary churn here.

Two ref-based patterns are intentionally left as-is (documented in code comments) because "fixing" them would change behavior:

  • The navigation-lock ref in TokenDetailsActions — converting it to state would make the double-tap guard async/batched, which could let a rapid second tap through during a slow render.
  • The perps-market reset in TokenDetails — a useEffect version raced with a child component's own effect and broke TOKEN_DETAILS_OPENED tracking (caught by existing tests).

Measured with React DevTools profiling (tap into the same token 5x, before/after): PriceChartProvider/Transactions render count dropped from 6 to 3 per visit, and every remaining render now maps to a real data change (loading state, chart price direction resolving, perps market resolving) instead of a phantom re-render caused by the lack of memoization.

Note: this doesn't move the Sentry AssetDetails trace metric itself, since that trace ends on first mount, before any of this re-render activity happens. The improvement is to post-mount jank/perceived loading smoothness, not the traced metric.

Changelog

CHANGELOG entry: null

Related issues

Fixes: N/A — investigative performance work, no linked ticket.

Manual testing steps

Feature: Asset Details screen

  Scenario: viewing a token's details
    Given the user is on the wallet home screen
    When the user taps a token to open Asset Details
    Then the chart, balance, price and transaction list render correctly
    And no extra flicker or jank is introduced versus main

  Scenario: using the action buttons
    Given the user is on the Asset Details screen for a token
    When the user taps Buy, Send, Receive, or More
    Then the correct flow opens and the buttons remain protected against rapid double-taps

  Scenario: perps Long/Short with compliance gate
    Given the user is on the Asset Details screen for a token with a perps market
    When the user taps Long or Short
    Then the geo-block or compliance modal (if applicable) opens correctly
    And pressing the button again afterward works instead of staying stuck locked

  Scenario: viewing on a block explorer
    Given the user is on the Asset Details screen
    When the user opens "View on block explorer" from the transactions list
    Then the correct explorer URL opens for both EVM and non-EVM chains

Screenshots/Recordings

N/A — this is a render-behavior/perf fix with no visual/UI changes.

Before

After

Pre-merge author checklist

Performance checks (if applicable)

  • I've tested on Android
    • Ideally on a mid-range device; emulator is acceptable
  • I've tested with a power user scenario
    • Use these power-user SRPs to import wallets with many accounts and tokens
  • I've instrumented key operations with Sentry traces for production performance metrics

For performance guidelines and tooling, see the Performance Guide.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

… Asset Details

The Asset Details chart/transactions subtree (PriceChartProvider,
AssetOverviewContent) was re-rendering 6 times per visit instead of being
memoized by React Compiler, because several components had Rules-of-React
violations (refs read during render, throw/value-blocks inside try/catch)
that made the compiler skip them entirely.

Fixes the compiler bailouts across TokenDetails (Views + AssetOverview),
AssetOverviewContent, TokenDetailsActions, and Transactions/index.js by
moving ref writes into effects, converting a render-time-read ref to state,
and rewriting a few try/catch bodies to avoid throw/optional-chaining/ternary
value blocks the compiler can't yet handle. Verified against the actual
React Compiler that every rewrite is necessary (reverting any one of them
reintroduces its bailout) and that none of it is over-fixing.

Two ref-based patterns are kept as intentional, documented bailouts because
converting them would change behavior: the navigation-lock ref in
TokenDetailsActions (a state-based lock would be async/batched and could
let a double-tap through), and the perps-market reset in TokenDetails
(a useEffect version raced with a child effect and broke tracking).

Verified with React DevTools profiling: PriceChartProvider/Transactions
render count dropped from 6 to 3 per visit, and every remaining render now
maps to a real data change rather than an unmemoized render.
@juanmigdr juanmigdr self-assigned this Aug 14, 2026
@metamask-ci

metamask-ci Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

PR template — items to address before "Ready for review"

Warnings — informational, address before merging:

  • Pre-merge author checklist has unchecked items (e.g. "I've applied the right labels on the PR (see labeling guidelines). Not required for external contributors."). Every box must be consciously checked — see docs/readme/ready-for-review.md.

See docs/readme/ready-for-review.md for the full Definition of Ready for Review.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Smart E2E Test Selection

  • Selected E2E tags: SmokeWalletPlatform, SmokeConfirmations, SmokePerps
  • Selected Performance tags: None (no tests recommended)
  • Risk Level: medium
  • AI Confidence: 78%
click to see 🤖 AI reasoning details

E2E Test Selection:
The PR contains React Compiler compatibility fixes and bug fixes across two main areas:

  1. TokenDetails components (AssetOverview/TokenDetails/TokenDetails.tsx, TokenDetails/Views/TokenDetails.tsx, AssetOverviewContent.tsx, TokenDetailsActions.tsx):

    • Refactoring ternary expressions to if/else for CAIP chain ID handling and market data fetching
    • useLayoutEffect wrapping for fireClosedRef.current assignment (analytics tracking fix)
    • useMemo replacing ref-during-render pattern for market insights trace start
    • caip19AssetId useMemo logic refactored for null safety
    • TokenDetailsActions.tsx changes are comment-only (cosmetic, no functional impact)
    • These affect the Token Details screen which is the entry point for Perps actions → SmokePerps + SmokeWalletPlatform
  2. Transactions/index.js (significant behavioral changes):

    • existingTxRef (ref) → existingTx (state): This changes how the speed-up/cancel modal receives the transaction object, now properly reactive
    • useLayoutEffect for toggleDetailsViewRef assignment
    • Block explorer error handling changed from throw→catch to Logger.error+return (prevents crashes on missing explorer)
    • EIP-1559 detection refactored from ternary to if/else
    • These affect transaction history display, speed-up/cancel modal, and block explorer navigation → SmokeWalletPlatform (transaction history) + SmokeConfirmations (speed-up/cancel flows)

Tag selection rationale:

  • SmokeWalletPlatform: Covers transaction history display (incoming/outgoing ETH transactions, token transfer details) and the Trending/browser navigation integration. The Transactions/index.js changes directly affect the transaction list view.
  • SmokeConfirmations: Covers transaction sending and the speed-up/cancel modal flows. The existingTxRef→state change and EIP-1559 detection refactoring affect these flows.
  • SmokePerps: The TokenDetailsActions component is the entry point for Perps actions from the token detail screen. Changes to AssetOverviewContent and TokenDetails affect the perps flow entry.

Per SmokePerps tag description: also select SmokeWalletPlatform (Trending section) and SmokeConfirmations (Add Funds deposits are on-chain transactions) — both already selected.

Performance Test Selection:
The changes are React Compiler compatibility fixes and bug fixes (ternary→if/else refactoring, ref→state migration, useLayoutEffect wrapping). None of these changes affect app launch, onboarding, login, asset loading performance, or any performance-sensitive initialization paths. The Transactions/index.js changes improve correctness of the speed-up/cancel modal but don't affect rendering performance metrics. No performance test tags are warranted.

View GitHub Actions results

@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
70.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant