If you can use React Hooks, a good approach is to useEffect
, so the event listener will be subscribed only once and properly unsubscribed when the component is unmounted.
The example below was extracted from https://usehooks.com/useEventListener/:
// Hook
function useEventListener(eventName, handler, element = window){
// Create a ref that stores handler
const savedHandler = useRef();
// Update ref.current value if handler changes.
// This allows our effect below to always get latest handler ...
// ... without us needing to pass it in effect deps array ...
// ... and potentially cause effect to re-run every render.
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(
() => {
// Make sure element supports addEventListener
// On
const isSupported = element && element.addEventListener;
if (!isSupported) return;
// Create event listener that calls handler function stored in ref
const eventListener = event => savedHandler.current(event);
// Add event listener
element.addEventListener(eventName, eventListener);
// Remove event listener on cleanup
return () => {
element.removeEventListener(eventName, eventListener);
};
},
[eventName, element] // Re-run if eventName or element changes
);
};
You also could install it from npm, for example, npm i @use-it/event-listener
- see the project here - https://github.com/donavon/use-event-listener.
Then, to use it in your component you just have to call it inside your functional component passing the event name and the handler. For example, if you want to console.log
every time the Escape key is pressed:
import useEventListener from '@use-it/event-listener'
const ESCAPE_KEYS = ['27', 'Escape'];
const App = () => {
function handler({ key }) {
if (ESCAPE_KEYS.includes(String(key))) {
console.log('Escape key pressed!');
}
}
useEventListener('keydown', handler);
return <span>hello world</span>;
}