Skip to content

Add cancellation (AbortController / cancelled flag) to async effects that setState after resolve #31495

Description

@MajorLift

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:

  • app/components/Views/ConnectQRHardware/index.tsx:129KeyringController.getAccounts().then(...)
  • app/components/Views/LedgerSelectAccount/index.tsx:135 and :210
  • app/components/Views/RevealPrivateCredential/RevealPrivateCredential.tsx:139
  • remaining 12 hits: re-run the detection recipe and triage (some may be guarded upstream)

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    To be fixed

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions