Performance audit finding · Severity: Medium (batch) · Effort: Medium · Fix risk: Simple · Test safety net: Partial
Owner: per-file CODEOWNERS
Scope: 16 sweep hits app-wide
What is this about?
Async effects that fetch(...)/.then(...) and set state on resolve, with no cancelled flag or AbortController, keep running after unmount (wasted work, "setState on unmounted component") and race when the driving input changes quickly (stale response overwrites fresh data). Sweep on main (b3e7eb88) found 16 candidate sites. Verified instances:
Detection recipe:
grep -rn -A6 "useEffect(" app --include="*.ts" --include="*.tsx" | grep -E "fetch\(|\.then\(" | grep -v "signal\|cancelled\|abort" | grep -v ".test."
Technical Details
Extract shared hooks (useIsMounted, useAbortableEffect) so cleanup is default.
Fix per site: cancelled-flag cleanup (cheapest, works for any promise) or AbortController (also cancels the request; RN fetch supports signal).
Acceptance Criteria
- All 16 hits triaged: fixed or waived with a stated reason (e.g. app-lifetime component).
- No "setState on unmounted component" warnings when rapidly entering/leaving the affected screens.
References
What is this about?
Async effects that
fetch(...)/.then(...)and set state on resolve, with no cancelled flag orAbortController, keep running after unmount (wasted work, "setState on unmounted component") and race when the driving input changes quickly (stale response overwrites fresh data). Sweep onmain(b3e7eb88) found 16 candidate sites. Verified instances:app/components/Views/ConnectQRHardware/index.tsx:129—KeyringController.getAccounts().then(...)app/components/Views/LedgerSelectAccount/index.tsx:135and:210app/components/Views/RevealPrivateCredential/RevealPrivateCredential.tsx:139Detection recipe:
grep -rn -A6 "useEffect(" app --include="*.ts" --include="*.tsx" | grep -E "fetch\(|\.then\(" | grep -v "signal\|cancelled\|abort" | grep -v ".test."Technical Details
Extract shared hooks (
useIsMounted,useAbortableEffect) so cleanup is default.Fix per site: cancelled-flag cleanup (cheapest, works for any promise) or
AbortController(also cancels the request; RN fetch supportssignal).Acceptance Criteria
References
mms-performancesweep recipes added in perf: Augmentmms-performancewith frontend learnings from the Extension performance audit skills#49 (mm-useeffect-antipatterns)