From 420945e081e92363463d69dcb17da97c32272bf3 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 19:54:50 -0700 Subject: [PATCH] fix(web): stabilize useKeyboardShortcuts with ref-based callback - Use useRef to hold shortcuts array, read from ref inside event handler - Event listener registered once on mount (empty deps), avoids re-subscription when callers forget to memoize the shortcuts array - Prevents subtle memory leak from rapid add/remove of keydown listeners --- web/src/lib/use-keyboard-shortcuts.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/web/src/lib/use-keyboard-shortcuts.ts b/web/src/lib/use-keyboard-shortcuts.ts index 2d8e12d..319665e 100644 --- a/web/src/lib/use-keyboard-shortcuts.ts +++ b/web/src/lib/use-keyboard-shortcuts.ts @@ -1,6 +1,6 @@ "use client"; -import { useEffect } from "react"; +import { useEffect, useRef } from "react"; export interface KeyboardShortcut { key: string; @@ -12,6 +12,9 @@ export interface KeyboardShortcut { } export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[]) { + const shortcutsRef = useRef(shortcuts); + shortcutsRef.current = shortcuts; + useEffect(() => { function handleKeyDown(event: KeyboardEvent) { const target = event.target as HTMLElement; @@ -21,7 +24,7 @@ export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[]) { target.tagName === "SELECT" || target.isContentEditable; - for (const shortcut of shortcuts) { + for (const shortcut of shortcutsRef.current) { const metaMatch = shortcut.meta ? event.metaKey || event.ctrlKey : !event.metaKey && !event.ctrlKey; const shiftMatch = shortcut.shift ? event.shiftKey : !event.shiftKey; const altMatch = shortcut.alt ? event.altKey : !event.altKey; @@ -39,5 +42,5 @@ export function useKeyboardShortcuts(shortcuts: KeyboardShortcut[]) { window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); - }, [shortcuts]); + }, []); }