bugfix: reject enable promise on user rejection#6671
Merged
Conversation
Collaborator
danfinlay
approved these changes
May 31, 2019
Merged
7 tasks
github-merge-queue bot
pushed a commit
that referenced
this pull request
Mar 11, 2026
…#39309) ## **Description** `ShieldSubscriptionProvider` was creating a new context value object on every render and recreating `evaluateCohortEligibility` whenever any of its 10 dependencies changed, causing unnecessary re-renders of the Home component subtree. Two fixes applied: 1. **Stabilized callback with ref pattern** -- Moved the `evaluateCohortEligibility` implementation into a ref updated via `useEffect`. The public callback delegates to the ref, keeping a stable reference (`useCallback` with empty deps) while still accessing current closure values. 2. **Memoized context value** -- Wrapped the provider value object in `useMemo` keyed on the now-stable callback, preventing a new object reference on every render. This is fix 6 of 6 in the [P0 Global Cascade Re-render Fixes epic](https://github.com/MetaMask/MetaMask-planning/issues/6669) ([RCA](https://docs.google.com/document/d/1KrzqL1IEQb-ctOKpOxhjBVr_-5jDXTgwC8a4r1ICBg0/edit?tab=t.5g1bh4tznel0), [Profiling Report](https://docs.google.com/document/d/1zlTg8rWYUFKZAsjQF3I9o7KaMA6uq7qcYjdfWKIZIJU/edit?tab=t.0#heading=h.dxmx8vunwu2s)). Together, the 6 memoization fixes break the cascade chain originating in Routes/Home that propagated through the entire component tree on every navigation and state update. [](https://codespaces.new/MetaMask/metamask-extension/pull/39309?quickstart=1) ## **Profiling Results** (all 6 fixes combined -- [Full Report](https://docs.google.com/document/d/1zlTg8rWYUFKZAsjQF3I9o7KaMA6uq7qcYjdfWKIZIJU/edit?tab=t.0#heading=h.dxmx8vunwu2s)) We ran 7 rounds of controlled A/B benchmarks over 41 hours. Results confirm the cascade was the dominant source of interactive UI latency. WDYR audit: 0 cascade re-renders on treatment (RCA measured 125-200 per action on baseline). > Measured on test builds (`yarn build:test`) with React StrictMode, Power User persona (30 accounts). All p-values from Welch's t-test, confirmed by Mann-Whitney U. Effect sizes: Cohen's d = 0.93-5.11 (large to very large). n=5-20 per metric. ### Per-interaction improvements | Flow | Baseline | Treatment | Improvement | p | |------|----------|-----------|-------------|---| | Account switching (5 accts) | 3.1s | 1.7s | **-1.4s, 47% faster** | 0.044 | | Asset detail nav (ETH) | 83ms | 37ms | **-46ms, 55% faster** | 0.044 | | Asset detail nav (SOL) | 75ms | 31ms | **-44ms, 59% faster** | <0.001 | | Send flow (e2e) | 928ms | 804ms | -124ms, 13% faster | 0.044 | | Send: token select to form | 25ms | 13ms | **-12ms, 49% faster** | 0.040 | ### Cascade compounding under sustained interaction Route cycling benchmark (3 round-trips: Send -> Assets -> Swap): | Segment | Baseline | Treatment | Improvement | p | |---------|----------|-----------|-------------|---| | **Total (6 transitions)** | **19.9s** | **2.7s** | **-17.2s, 7x faster** | 0.004 | | sendToHome | 6.4s | 723ms | -5.7s, 89% faster | 0.018 | | assetDetailsToHome | 6.3s | 659ms | -5.7s, 90% faster | 0.004 | | swapToHome | 5.0s | 787ms | -4.2s, 84% faster | 0.001 | | homeToSwap | 877ms | 227ms | -650ms, 74% faster | 0.002 | | homeToAssetDetails | 628ms | 236ms | -392ms, 62% faster | 0.004 | | homeToSend | 673ms | 82ms | -591ms, 88% faster | 0.046 | The 7x is a **compounding effect** across successive transitions, not a per-transition effect. Isolated transitions show only 10-15% improvement. Each navigation accumulates subscription/listener state on baseline, escalating re-render overhead -- consistent with the RCA's cascade chain model (Routes -> Home -> all children). This compounding behavior is itself a key finding: it means any remaining or future cascade leak will degrade **non-linearly** with session length, even if it looks trivial in a single benchmark. **Production estimate:** StrictMode roughly doubles render work. The 7x measured in dev builds is expected to land at ~2-7x in production depending on wallet size, tree depth, and GC timing. ### Metrics to watch after merge - **CI (`metamask-performance`):** `assetClickToPriceChart` should show a step-change (83->37ms dev). `cascadeFixProfiling` preset can be added for ongoing regression tracking. - **Production Sentry (RUM):** Best signal at **p75/p95 navigation latency** and **INP p95**, segmented by users with 10+ accounts. Expect improvement on route transitions, account switching, token list rendering. Signal will be diffuse due to network variance and device heterogeneity -- don't expect a clean step-function. - **TBT:** Cascade generates clusters of consecutive long tasks (>50ms) on every state change. Fix eliminates these. Should show up in Sentry performance dashboard once long-task tracking is enabled. ## **Changelog** CHANGELOG entry: Fix memoization issue in top-level context provider that was causing cascading re-renders. ## **Related issues** Fixes: MetaMask/MetaMask-planning#6638 Part of epic: [MetaMask-planning#6669 -- P0 Global Cascade Re-render Fixes](https://github.com/MetaMask/MetaMask-planning/issues/6669) Sibling PRs: - [#39310](#39310) -- Memoize MetaMetrics context ([#6640](https://github.com/MetaMask/MetaMask-planning/issues/6640)) - [#39311](#39311) -- Stabilize `withRouterHooks` params/location ([#6671](https://github.com/MetaMask/MetaMask-planning/issues/6671)) - [#39312](#39312) -- Fix `getPendingApprovals` selector ([#6673](https://github.com/MetaMask/MetaMask-planning/issues/6673)) - [#39313](#39313) -- Fix `pendingConfirmationsSortedSelector` ([#6674](https://github.com/MetaMask/MetaMask-planning/issues/6674)) - [#39314](#39314) -- Remove dead What's New modal code ([#6670](https://github.com/MetaMask/MetaMask-planning/issues/6670)) ## **Manual testing steps** ## **Screenshots/Recordings** ## **Pre-merge author checklist** - [x] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [x] I've completed the PR template to the best of my ability - [x] I've included tests if applicable - [x] I've documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [x] I've applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **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. <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes how `evaluateCohortEligibility` is wired (ref-backed stable callback) and how the context value is memoized, which could alter when/with-what state eligibility checks run if the ref is not kept in sync. Added tests reduce risk but this touches subscription/cohort gating that affects modal display behavior. > > **Overview** > Reduces cascading re-renders from `ShieldSubscriptionProvider` by making the exported `evaluateCohortEligibility` function reference-stable and memoizing the context `value` object. > > Refactors `evaluateCohortEligibility` to delegate through a `useRef`-stored implementation (updated during render so it’s available in commit-phase callbacks) while keeping the public callback `useCallback([])`. Adds a new Jest test suite validating child rendering, stable callback/context identity across re-renders, and that the stable callback still observes updated selector values. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 50a2fed. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Jongsun Suh <jongsun.suh@icloud.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request checks for both types of possible RPC errors (the
errorargument andresult.error) and rejects thePromisereturned byethereum.enableaccordingly.Fixes #6631