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]);