diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx index 597162e..03a31fe 100644 --- a/src/components/Modal.jsx +++ b/src/components/Modal.jsx @@ -8,10 +8,17 @@ export default function Modal({ title, children, onClose, maxWidth = 'max-w-lg' const dialogRef = useRef(null); const previouslyFocused = useRef(null); + // Keep the latest onClose in a ref so the mount-only effect below can call it + // without listing onClose as a dependency. Callers pass a fresh inline arrow + // each render; depending on it would re-run the effect on every keystroke and + // steal focus back to the dialog (kicking the user out of input fields). + const onCloseRef = useRef(onClose); + onCloseRef.current = onClose; + useEffect(() => { previouslyFocused.current = document.activeElement; const handleKey = (e) => { - if (e.key === 'Escape') onClose?.(); + if (e.key === 'Escape') onCloseRef.current?.(); }; document.addEventListener('keydown', handleKey); // Focus the dialog so screen readers announce it and keyboard focus lands inside @@ -20,7 +27,7 @@ export default function Modal({ title, children, onClose, maxWidth = 'max-w-lg' document.removeEventListener('keydown', handleKey); previouslyFocused.current?.focus?.(); }; - }, [onClose]); + }, []); return (