I am making a gallery app in React and I stucked 'cuz when I want to change img by key (left & right arrow) React/browser/JS? duplicates listener, as you can see:
The code looks like this:
const handleKey = (e) => {
console.log(`user has pressed ${e.key}`);
if (e.key === 'ArrowLeft') {
previousPicture();
}
if (e.key === 'ArrowRight') {
nextPicture();
}
};
useEffect(() => {
document.addEventListener('keydown', handleKey);
return () => window.removeEventListener('keydown', handleKey);
});
I tried to:
- paste handleKey inside useEffect;
- use empty dependencies array;
- paste into dep array activeIndex;
- make it in this way: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Multiple_identical_event_listeners
When I tried e.g. this, app worked fine, but it was necessary to press TAB & then using arrows.
<Wrapper isOpen={isOpen} tabIndex={0} onKeyDown={handleKey}>
// content...
</Wrapper>
When I am navigating/changing images by buttons everything works well.
EDIT: I modified code and added useCallback (thanks kind user). The 'duplicate issue' is gone, but I can't navigate with arrows. I tried to use in nextPic/prevPic callbacks: [activeIndex], [], and no dep array and still doesn't work.
I've created sandbox: https://codesandbox.io/embed/dry-frog-tn86k2
CodePudding user response:
use empty dependencies array;
Leaving the dependency array empty will have no expected effect, since you need to have the reference to the handler function.
You'd have to wrap the handleKey function with useCallback hook to persist the reference between renders, so the cleanup that takes place in useEffect points to the right handleKey function instance.
Note: You'd probably have to wrap the previousPicture and nextPicture with useCallback as well. Depends how it is initialized.
const handleKey = useCallback((e) => {
console.log(`user has pressed ${e.key}`);
if (e.key === 'ArrowLeft') {
previousPicture();
}
if (e.key === 'ArrowRight') {
nextPicture();
}
}, []);
useEffect(() => {
document.addEventListener('keydown', handleKey);
return () => window.removeEventListener('keydown', handleKey);
}, [handleKey]);

