Skip to content

Add cleanup to timers and listeners registered in effects (batch sweep) #31509

Description

@MajorLift

Performance audit finding · Severity: Medium (batch) · Effort: Medium · Fix risk: Simple · Test safety net: Partial
Owner: per-file CODEOWNERS
Scope: timers and listeners registered in effects without cleanup, app-wide — GitHub code search sizes the triage surface at 39 files co-locating setInterval with useEffect and 34 files co-locating addEventListener with useEffect under app/ (file-level co-occurrence; each hit needs a teardown check, many will be fine)

What is this about?

Every setInterval, setTimeout, and addEventListener inside a useEffect must be torn down in the effect's cleanup function. A leaked interval keeps firing (and dispatching) after unmount; a leaked listener pins its closure — and everything the closure captured — out of garbage collection. Mount/unmount cycles stack additional copies. The extension's equivalent sweep (MetaMask-planning#6541) found 122 instances; mobile's known anchors:

Detection recipe (from the skill's mm-useeffect-antipatterns / audit playbook):

grep -rn "setInterval\|setTimeout\|addEventListener\|\.on(" app --include="*.ts" --include="*.tsx" | grep -v ".test." | grep -vE "clear|remove|off\("

For each hit, confirm a matching teardown (return () => … with clearInterval/clearTimeout/removeEventListener/.off()); fix or waive with a stated reason (app-lifetime singletons).

Scenario

N/A — see Technical Details.

Design

N/A — internal performance change; no UI/design impact.

Technical Details

Fix per site: return the teardown from the effect. Once patterns repeat, prefer the shared-hook route already prescribed in #31495 (useEventListener with automatic cleanup, per the extension's MetaMask-planning#7166) so cleanup is the default rather than per-site diligence.

Threat Modeling Framework

N/A — performance-only change; behavior is preserved, no new data flow / trust boundary / attack surface.

Acceptance Criteria

  • All sweep hits triaged: teardown added or waived with reason.
  • Navigate-in/navigate-out N times on touched screens: timer/listener counts stay flat (heap snapshot or js-memory-leaks recipe); no post-unmount dispatches.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    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