From 9bcb6659a0c8537908a6597b5e9c34e9ee3bb109 Mon Sep 17 00:00:00 2001 From: Muhammed Teuvazhukov Date: Wed, 7 May 2025 15:13:07 +0300 Subject: [PATCH 1/2] Add AbortController to clean up event listeners - This commit fixes the main problem with memory leak - Also, this ensures proper cleanup of the event listener by aborting it with a signal. It improves reliability and prevents potential memory leaks or unexpected behavior --- src/FixedHolder/index.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/FixedHolder/index.tsx b/src/FixedHolder/index.tsx index f2a4a6331..3db03db28 100644 --- a/src/FixedHolder/index.tsx +++ b/src/FixedHolder/index.tsx @@ -82,6 +82,8 @@ const FixedHolder = React.forwardRef>((pro }, []); React.useEffect(() => { + const abortController = new AbortController(); + function onWheel(e: WheelEvent) { const { currentTarget, deltaX } = e as unknown as React.WheelEvent; if (deltaX) { @@ -104,10 +106,13 @@ const FixedHolder = React.forwardRef>((pro e.preventDefault(); } } - scrollRef.current?.addEventListener('wheel', onWheel, { passive: false }); + scrollRef.current?.addEventListener('wheel', onWheel, { + passive: false, + signal: abortController.signal, + }); return () => { - scrollRef.current?.removeEventListener('wheel', onWheel); + abortController.abort(); }; }, []); From e971bb1faf3e41ea2714f1ead1bc3ae09b5b74db Mon Sep 17 00:00:00 2001 From: Muhammed Teuvazhukov Date: Wed, 7 May 2025 15:14:03 +0300 Subject: [PATCH 2/2] Add AbortController to clean up event listeners - Change other addEventListeners to AbortController way for same code style across listeners --- src/stickyScrollBar.tsx | 42 +++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/src/stickyScrollBar.tsx b/src/stickyScrollBar.tsx index 284b25085..5001f791d 100644 --- a/src/stickyScrollBar.tsx +++ b/src/stickyScrollBar.tsx @@ -141,18 +141,25 @@ const StickyScrollBar: React.ForwardRefRenderFunction { - document.body.addEventListener(MOUSEUP_EVENT, onMouseUp, false); - document.body.addEventListener(MOUSEMOVE_EVENT, onMouseMove, false); + const abortController = new AbortController(); + document.body.addEventListener(MOUSEUP_EVENT, onMouseUp, { + capture: false, + signal: abortController.signal, + }); + document.body.addEventListener(MOUSEMOVE_EVENT, onMouseMove, { + capture: false, + signal: abortController.signal, + }); checkScrollBarVisible(); return () => { - document.body.removeEventListener(MOUSEUP_EVENT, onMouseUp); - document.body.removeEventListener(MOUSEMOVE_EVENT, onMouseMove); + abortController.abort(); }; }, [scrollBarWidth, isActive]); // Loop for scroll event check React.useEffect(() => { if (!scrollBodyRef.current) return; + const abortController = new AbortController(); const scrollParents: (HTMLElement | SVGElement)[] = []; let parent = getDOM(scrollBodyRef.current); @@ -161,16 +168,27 @@ const StickyScrollBar: React.ForwardRefRenderFunction p.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false)); - window.addEventListener(RESIZE_EVENT, checkScrollBarVisible, false); - window.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false); - container.addEventListener(SCROLL_EVENT, checkScrollBarVisible, false); + scrollParents.forEach(p => + p.addEventListener(SCROLL_EVENT, checkScrollBarVisible, { + capture: false, + signal: abortController.signal, + }), + ); + window.addEventListener(RESIZE_EVENT, checkScrollBarVisible, { + capture: false, + signal: abortController.signal, + }); + window.addEventListener(SCROLL_EVENT, checkScrollBarVisible, { + capture: false, + signal: abortController.signal, + }); + container.addEventListener(SCROLL_EVENT, checkScrollBarVisible, { + capture: false, + signal: abortController.signal, + }); return () => { - scrollParents.forEach(p => p.removeEventListener(SCROLL_EVENT, checkScrollBarVisible)); - window.removeEventListener(RESIZE_EVENT, checkScrollBarVisible); - window.removeEventListener(SCROLL_EVENT, checkScrollBarVisible); - container.removeEventListener(SCROLL_EVENT, checkScrollBarVisible); + abortController.abort(); }; }, [container]);