In React Hooks documents it is shown how to removeEventListener during the component\'s cleanup phase. https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-
You can put the handleKeyUp
function inside of the function given to useEffect
and only add the listener and return a cleanup function when collapsed
is false.
useEffect(() => {
if (!collapsed) {
function handleKeyUp(event) {
switch (event.key) {
case "Escape":
setCollapsed(true);
break;
}
}
window.addEventListener("keyup", handleKeyUp);
return () => window.removeEventListener("keyup", handleKeyUp);
}
}, [collapsed]);
Tholle's answer may work, but it's bad practice to declare a function inside an if
.
It makes it harder to follow when the function is declared and when it is not. Also it can lead to bugs because functions are hoisted up.
There's a neater way to fix it:
By wrapping your event handler with the useCallback hook.
const [collapsed, setCollapsed] = useState(true)
const handleKeyUp = useCallback((event) => {
if (event.key === "Escape") {
setCollapsed(true)
}
}, [setCollapsed])
useEffect(() => {
if (!collapsed) {
window.addEventListener("keyup", handleKeyUp)
} else {
window.removeEventListener("keyup", handleKeyUp)
}
return () => window.removeEventListener("keyup", handleKeyUp)
}, [collapsed, handleKeyUp])
useCallback
has a dependency on setCollapsed
. This makes sure handleKeyUp
is not redefined when the component rerenders (which always happens when state changes)useEffect
will conditionally add/remove the event listener, otherwise events will keep firing as long as the component is mounted.If you use a lot of event handlers in useEffect, there's a custom hook for that: https://usehooks.com/useEventListener/
Here's the question posters example updated with my solution: https://codepen.io/publicJorn/pen/eYzwENN