Skip to content

Conversation

@runway-github
Copy link
Contributor

@runway-github runway-github bot commented Nov 28, 2025

Description

Reverts the following change made to useMultiPolling hook in
f167e26
(#37480), where a
cleanup function that should only run on unmount was moved into an
effect with dependencies.

diff --git a/ui/hooks/useMultiPolling.ts b/ui/hooks/useMultiPolling.ts
index c28967ef2b..ce61598753 100644
--- a/ui/hooks/useMultiPolling.ts
+++ b/ui/hooks/useMultiPolling.ts
@@ -44,10 +44,7 @@ const useMultiPolling = <PollingInput>(
         pollingTokens.current.delete(inputKey);
       }
     }
-  }, [
-    completedOnboarding,
-    usePollingOptions.input && JSON.stringify(usePollingOptions.input),
-  ]);
+  }
 
   // stop all polling on dismount
   useEffect(() => {
@@ -56,7 +53,10 @@ const useMultiPolling = <PollingInput>(
         usePollingOptions.stopPollingByPollingToken(token);
       }
     };
-  }, []);
+  }, [[
+    completedOnboarding,
+    usePollingOptions.input && JSON.stringify(usePollingOptions.input),
+  ]]);
 };
 
 export default useMultiPolling;

This change causes cleanup to run on every dependency change as well as
on unmount, triggering the following race conditions:

  1. Premature token cleanup
    When inputs change, cleanup stops all tokens before the effect body
    runs. This could stop tokens for inputs that didn't change, causing
    unnecessary stop/restarts.

  2. Stale tokens
    Dependency changes can potentially trigger cleanup while
    startPolling().then() calls are still pending. This could cause
    pending .then() to store stale tokens that were already cleaned up.

This commit resolves these issues by moving the cleanup function back
into an effect with an empty dependency array so that it only runs on
unmount.

The following behavior is restored:

  • Only polling calls for removed inputs are stopped on cleanup.
  • Pending starts are not interrupted by cleanup during the hook
    lifecycle.

Note that usePolling has its cleanup run on dependency changes,
because it's only managing a single poll at a time. Moving usePolling
cleanup to unmount-only would cause multiple simultaneous polls and
orphaned tokens.

Open in GitHub Codespaces

Changelog

CHANGELOG entry: null

Related issues

Manual testing steps

  1. Go to this page...

Screenshots/Recordings

Before

After

Pre-merge author checklist

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.

Note

Moves polling cleanup to an unmount-only effect and removes dependency-based cleanup to avoid premature/stale token issues.

  • Hooks:
    • ui/hooks/useMultiPolling.ts:
      • Move "stop all polling" cleanup to a mount/unmount effect ([] deps) and reset prevPollingInputStringified/isMounted on unmount.
      • Remove cleanup return from the polling effect; now only starts/stops per-input based on diffs during lifecycle.
      • Simplify early return when onboarding incomplete or inputs unchanged.

Written by Cursor Bugbot for commit 129fa16. This will update automatically on new commits. Configure here.

5957159

…n `useMultiPolling` effect cleanup (#38405)

<!--
Please submit this PR as a draft initially.
Do not mark it as "Ready for review" until the template has been
completely filled out, and PR status checks have passed at least once.
-->

## **Description**

<!--
Write a short description of the changes included in this pull request,
also include relevant motivation and context. Have in mind the following
questions:
1. What is the reason for the change?
2. What is the improvement/solution?
-->

Reverts the following change made to `useMultiPolling` hook in
f167e26
(#37480), where a
cleanup function that should only run on unmount was moved into an
effect with dependencies.

```diff
diff --git a/ui/hooks/useMultiPolling.ts b/ui/hooks/useMultiPolling.ts
index c28967e..ce61598753 100644
--- a/ui/hooks/useMultiPolling.ts
+++ b/ui/hooks/useMultiPolling.ts
@@ -44,10 +44,7 @@ const useMultiPolling = <PollingInput>(
         pollingTokens.current.delete(inputKey);
       }
     }
-  }, [
-    completedOnboarding,
-    usePollingOptions.input && JSON.stringify(usePollingOptions.input),
-  ]);
+  }
 
   // stop all polling on dismount
   useEffect(() => {
@@ -56,7 +53,10 @@ const useMultiPolling = <PollingInput>(
         usePollingOptions.stopPollingByPollingToken(token);
       }
     };
-  }, []);
+  }, [[
+    completedOnboarding,
+    usePollingOptions.input && JSON.stringify(usePollingOptions.input),
+  ]]);
 };
 
 export default useMultiPolling;
```

This change causes cleanup to run on every dependency change as well as
on unmount, triggering the following race conditions:

1. Premature token cleanup
When inputs change, cleanup stops all tokens before the effect body
runs. This could stop tokens for inputs that didn't change, causing
unnecessary stop/restarts.

2. Stale tokens
Dependency changes can potentially trigger cleanup while
`startPolling().then()` calls are still pending. This could cause
pending `.then()` to store stale tokens that were already cleaned up.

This commit resolves these issues by moving the cleanup function back
into an effect with an empty dependency array so that it only runs on
unmount.

The following behavior is restored:
- Only polling calls for removed inputs are stopped on cleanup.
- Pending starts are not interrupted by cleanup during the hook
lifecycle.

Note that `usePolling` has its cleanup run on dependency changes,
because it's only managing a single poll at a time. Moving `usePolling`
cleanup to unmount-only would cause multiple simultaneous polls and
orphaned tokens.

[![Open in GitHub
Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/MetaMask/metamask-extension/pull/38405?quickstart=1)

## **Changelog**

<!--
If this PR is not End-User-Facing and should not show up in the
CHANGELOG, you can choose to either:
1. Write `CHANGELOG entry: null`
2. Label with `no-changelog`

If this PR is End-User-Facing, please write a short User-Facing
description in the past tense like:
`CHANGELOG entry: Added a new tab for users to see their NFTs`
`CHANGELOG entry: Fixed a bug that was causing some NFTs to flicker`

(This helps the Release Engineer do their job more quickly and
accurately)
-->

CHANGELOG entry: null

## **Related issues**

- Fixes issue introduced by:
#37480
- Followed by: #38339

## **Manual testing steps**

1. Go to this page...
2.
3.

## **Screenshots/Recordings**

<!-- If applicable, add screenshots and/or recordings to visualize the
before and after of your change. -->

### **Before**

<!-- [screenshots/recordings] -->

### **After**

<!-- [screenshots/recordings] -->

## **Pre-merge author checklist**

- [ ] 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).
- [ ] I've completed the PR template to the best of my ability
- [ ] I’ve included tests if applicable
- [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] 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]
> Moves polling cleanup to an unmount-only effect, ensuring all tokens
are stopped on dismount and removing cleanup from the dependency-based
effect.
> 
> - **Hooks/Lifecycle (useMultiPolling)**:
> - Add unmount-only cleanup: stop all polling tokens and reset
`prevPollingInputStringified`; manage `isMounted` flags.
> - Remove cleanup from dependency-driven effect; early-return becomes a
simple no-op.
> - **Polling management**:
> - Maintain logic to start polls for new inputs and stop polls for
removed inputs, guarding token storage with `isMounted`.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
0928d40. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@github-actions
Copy link
Contributor

CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes.

@metamaskbot metamaskbot added the team-bots Bot team (for MetaMask Bot, Runway Bot, etc.) label Nov 28, 2025
@metamaskbot
Copy link
Collaborator

Builds ready [129fa16]
UI Startup Metrics (1247 ± 97 ms)
PlatformBuildTypePageMetricMean (ms)Min (ms)Max (ms)Std Dev (ms)P 75 (ms)P 95 (ms)
ChromeBrowserifyStandard HomeuiStartup1247101514919713161420
load105086912599011081212
domContentLoaded104486412509011021205
domInteractive251586172276
firstPaint48890125839510341171
backgroundConnect21719624711226237
firstReactRender29207193148
getState3517104134058
initialActions104112
loadScripts833660104087891973
setupStore1265361423
numNetworkReqs1257720573
BrowserifyPower User HomeuiStartup21341740290426323712673
load1028894150413310241410
domContentLoaded1013889148613110051392
domInteractive36171512734111
firstPaint54910414974009701402
backgroundConnect276208827135245627
firstReactRender5940113136486
getState19513568074205267
initialActions1013112
loadScripts80068012641297881181
setupStore2111106122445
numNetworkReqs1426738173195289
WebpackStandard HomeuiStartup820686115386868978
load63456595975652790
domContentLoaded63056194675648785
domInteractive2715151252292
firstPaint23498956182196744
backgroundConnect12685131426
firstReactRender2919136143240
getState271369113547
initialActions105112
loadScripts62755893673646776
setupStore1144171328
numNetworkReqs1257821574
WebpackPower User HomeuiStartup16471267268828619092114
load679587100295689938
domContentLoaded66958099095680928
domInteractive33181302531106
firstPaint255931043184243678
backgroundConnect95969218931610
firstReactRender6243127126784
getState16712434226176201
initialActions108112
loadScripts66657898094678920
setupStore20660112345
numNetworkReqs1456739577204324
FirefoxBrowserifyStandard HomeuiStartup13191073186415414411584
load109092913539711661272
domContentLoaded108992913539711651272
domInteractive58311653179124
firstPaint------
backgroundConnect45211692946111
firstReactRender23175262435
getState127106121125
initialActions103122
loadScripts106391413359111321239
setupStore156163241145
numNetworkReqs1156916661
BrowserifyPower User HomeuiStartup25901980454757725994144
load1159943245622711491529
domContentLoaded1158943245622711481529
domInteractive1163247796109400
firstPaint------
backgroundConnect119271146168110451
firstReactRender59351532259111
getState27966875200380734
initialActions218123
loadScripts1107928230818611021442
setupStore1769782213164700
numNetworkReqs100553286582248
WebpackStandard HomeuiStartup15151229194615716081810
load12411074150610413151421
domContentLoaded12411074150610413151420
domInteractive64262173885133
firstPaint------
backgroundConnect46221912846114
firstReactRender28217893139
getState147194211333
initialActions103122
loadScripts1213105414829512891364
setupStore156160211347
numNetworkReqs1256716762
WebpackPower User HomeuiStartup29882247568179029704837
load14521147322838415622656
domContentLoaded14521147322738415622656
domInteractive10929525105108402
firstPaint------
backgroundConnect1782615002811351036
firstReactRender72392774269151
getState325571722278474874
initialActions203123
loadScripts13581095306828214561741
setupStore13381088187152618
numNetworkReqs996225352114242
📊 Page Load Benchmark Results

Current Commit: 129fa16 | Date: 11/28/2025

📄 Localhost MetaMask Test Dapp

Samples: 100

Summary

  • pageLoadTime-> current mean value: 1.05s (±77ms) 🟡 | historical mean value: 1.04s ⬆️ (historical data)
  • domContentLoaded-> current mean value: 727ms (±74ms) 🟢 | historical mean value: 726ms ⬆️ (historical data)
  • firstContentfulPaint-> current mean value: 81ms (±12ms) 🟢 | historical mean value: 81ms ⬆️ (historical data)

📈 Detailed Results

Metric Mean Std Dev Min Max P95 P99
pageLoadTime 1.05s 77ms 995ms 1.36s 1.29s 1.36s
domContentLoaded 727ms 74ms 681ms 1.01s 965ms 1.01s
firstPaint 81ms 12ms 68ms 188ms 92ms 188ms
firstContentfulPaint 81ms 12ms 68ms 188ms 92ms 188ms
largestContentfulPaint 0ms 0ms 0ms 0ms 0ms 0ms

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

Labels

team-bots Bot team (for MetaMask Bot, Runway Bot, etc.)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants