Skip to content

AbortSignal listeners accumulate when Object3D.clone() creates new uikit instances #258

@joaomelorodrigues

Description

@joaomelorodrigues

In latest uikit version the the js event listeners increased drastically.
I asked AI to sum up the issue i inspected with memory snapshots from my app.

abortableEffect adds "abort" listeners to AbortSignals. When Three.js clones Object3Ds that include uikit components (Fullscreen, Container, etc.), new uikit instances are created and each adds its own listener. These listeners are not cleaned up, so the listener count grows over time.

Environment
@react-three/uikit: 1.0.63
@react-three/xr: 6.6.29
@react-three/handle: 6.6.29
three: 0.183.1
react: 19.2.4
Reproduction
Use uikit components (e.g. Fullscreen, Container) inside an R3F Canvas wrapped with @react-three/xr’s XR.
Let the app run idle (no user interaction).
Monitor addEventListener calls or listener count.
Observed behavior
addEventListener("abort") is called repeatedly on AbortSignals. Stack traces point to:

GlyphGroupManager.init
PanelGroupManager.init
createDeferredRequestLayoutCalculation
abortableEffect
_Fullscreen.clone → _Object3D.copy
PerspectiveCamera.copy
FlexNode constructor
Example stack:

at EventTarget.addEventListener
at GlyphGroupManager.init (chunk-LDQN6KWB.js:5008:17)
at S.x (chunk-LDQN6KWB.js:5541:34)
...
at abortableEffect (chunk-LDQN6KWB.js:6077:15)
at buildRootContext
at new _Component
at new _Container
at new _Fullscreen
at _Fullscreen.clone
at _Object3D.copy
at PerspectiveCamera.copy
Root cause
When Object3D.clone() (or copy()) runs on objects that contain uikit components, new uikit instances are created. Each instance’s abortableEffect adds a listener to an AbortSignal. The cloned instances are not disposed correctly, so their listeners remain attached.

Expected behavior
Listeners added by abortableEffect should be removed when the component is disposed or when the cloned instance is no longer used. Alternatively, cloning should not create new uikit instances that add listeners without proper cleanup.

Possible fix directions
Ensure abort() is called on the AbortController when uikit components are disposed or when their Object3D is removed from the scene.
Avoid copying uikit-specific state (e.g. AbortController) in copy() so clones do not add new listeners.
Use a fresh AbortController per cloned instance and ensure it is aborted when the clone is disposed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions