From 3aa14e347948103602e423bd05f57923a11234bf Mon Sep 17 00:00:00 2001
From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Date: Mon, 26 Oct 2026 02:46:50 -0400
Subject: [PATCH 1/3] fix(footer): remove back-to-top listeners on unmount
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
---
.../fixed-footer-back-to-top-listeners.md | 1 +
src/__tests__/Footer.behavior.test.tsx | 23 +++++++++++++++++++
src/components/Footer.tsx | 17 ++++++++++----
3 files changed, 36 insertions(+), 5 deletions(-)
create mode 100644 changelog.d/fixed-footer-back-to-top-listeners.md
diff --git a/changelog.d/fixed-footer-back-to-top-listeners.md b/changelog.d/fixed-footer-back-to-top-listeners.md
new file mode 100644
index 00000000..f363c86d
--- /dev/null
+++ b/changelog.d/fixed-footer-back-to-top-listeners.md
@@ -0,0 +1 @@
+- Footer back-to-top effect now removes its scroll/click listeners on unmount.
diff --git a/src/__tests__/Footer.behavior.test.tsx b/src/__tests__/Footer.behavior.test.tsx
index 276ce9f0..fbd11230 100644
--- a/src/__tests__/Footer.behavior.test.tsx
+++ b/src/__tests__/Footer.behavior.test.tsx
@@ -216,4 +216,27 @@ describe("back-to-top button", () => {
fireEvent.click(button());
expect(scrollToSpy).toHaveBeenCalledWith({ top: 0, behavior: "smooth" });
});
+
+ it("removes scroll and click listeners on unmount", () => {
+ const removeEventListenerSpy = vi.spyOn(window, "removeEventListener");
+ const { unmount } = render();
+
+ expect(removeEventListenerSpy).not.toHaveBeenCalled();
+
+ unmount();
+
+ expect(removeEventListenerSpy).toHaveBeenCalledWith(
+ "scroll",
+ expect.any(Function)
+ );
+ const buttonSpy = vi.spyOn(button(), "removeEventListener");
+ // Re-render to get fresh spy after unmount; verify the handler was attached
+ render();
+ cleanup();
+ // After cleanup, the effect cleanup runs
+ expect(removeEventListenerSpy).toHaveBeenCalledWith(
+ "scroll",
+ expect.any(Function)
+ );
+ });
});
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index 901aca77..198dc6d1 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -35,20 +35,27 @@ export default function Footer() {
}
};
- window.addEventListener("scroll", toggleButton);
-
- backToTopButton.addEventListener("click", () => {
+ const handleClick = () => {
window.scrollTo({
top: 0,
behavior: "smooth",
});
- });
+ };
+
+ window.addEventListener("scroll", toggleButton);
+ backToTopButton.addEventListener("click", handleClick);
// Initial check
toggleButton();
+
+ // Cleanup function to prevent memory leaks
+ return () => {
+ window.removeEventListener("scroll", toggleButton);
+ backToTopButton.removeEventListener("click", handleClick);
+ };
};
- initBackToTop();
+ return initBackToTop();
}, []);
return (
From 54997d506fdfb07974a2cfec40355e8392cbed10 Mon Sep 17 00:00:00 2001
From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Date: Mon, 26 Oct 2026 02:49:28 -0400
Subject: [PATCH 2/3] test(footer): assert both listeners are removed on
unmount
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
---
src/__tests__/Footer.behavior.test.tsx | 20 ++++----------------
1 file changed, 4 insertions(+), 16 deletions(-)
diff --git a/src/__tests__/Footer.behavior.test.tsx b/src/__tests__/Footer.behavior.test.tsx
index fbd11230..92c6ce72 100644
--- a/src/__tests__/Footer.behavior.test.tsx
+++ b/src/__tests__/Footer.behavior.test.tsx
@@ -218,25 +218,13 @@ describe("back-to-top button", () => {
});
it("removes scroll and click listeners on unmount", () => {
- const removeEventListenerSpy = vi.spyOn(window, "removeEventListener");
+ const windowRemoveSpy = vi.spyOn(window, "removeEventListener");
const { unmount } = render();
-
- expect(removeEventListenerSpy).not.toHaveBeenCalled();
+ const buttonRemoveSpy = vi.spyOn(button(), "removeEventListener");
unmount();
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
- "scroll",
- expect.any(Function)
- );
- const buttonSpy = vi.spyOn(button(), "removeEventListener");
- // Re-render to get fresh spy after unmount; verify the handler was attached
- render();
- cleanup();
- // After cleanup, the effect cleanup runs
- expect(removeEventListenerSpy).toHaveBeenCalledWith(
- "scroll",
- expect.any(Function)
- );
+ expect(windowRemoveSpy).toHaveBeenCalledWith("scroll", expect.any(Function));
+ expect(buttonRemoveSpy).toHaveBeenCalledWith("click", expect.any(Function));
});
});
From dc9b32881eccc076609577bb068622ca96a089ce Mon Sep 17 00:00:00 2001
From: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
Date: Mon, 26 Oct 2026 02:55:19 -0400
Subject: [PATCH 3/3] style(footer): wrap long assertion for prettier
Signed-off-by: scanner <280983584+hivecommons-hive[bot]@users.noreply.github.com>
---
src/__tests__/Footer.behavior.test.tsx | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/__tests__/Footer.behavior.test.tsx b/src/__tests__/Footer.behavior.test.tsx
index 92c6ce72..b91fc591 100644
--- a/src/__tests__/Footer.behavior.test.tsx
+++ b/src/__tests__/Footer.behavior.test.tsx
@@ -224,7 +224,10 @@ describe("back-to-top button", () => {
unmount();
- expect(windowRemoveSpy).toHaveBeenCalledWith("scroll", expect.any(Function));
+ expect(windowRemoveSpy).toHaveBeenCalledWith(
+ "scroll",
+ expect.any(Function)
+ );
expect(buttonRemoveSpy).toHaveBeenCalledWith("click", expect.any(Function));
});
});