我在无状态组件中使用react
钩子。以下是源代码:
const MyComp = ({focused}) => {
...
const keyPressListener = (e: KeyboardEvent) => {
console.log('key press:', e);
};
useEffect(() => {
if (focused) {
console.log('add event listener');
window.addEventListener('keydown', keyPressListener);
} else {
console.log('remove event listener');
window.removeEventListener('keydown', keyPressListener);
}
}, [focused]);
...
}
它侦听酒店focused
。当它为真时,它会添加键下侦听器,当它变为假时,它会将其删除。我可以看到有关add/remove event listener
的控制台日志,但删除后仍会调用keyPressListener
函数。我看到很多人都有同样的问题,因为他们没有绑定函数。但我认为在这种情况下箭头功能不需要它。
每当focused
道具的值发生变化时,MyComp
都会重新渲染。每次重新渲染时,都会创建一个新的keyPressListener
函数。此新函数由 useEffect
挂钩在 keypress
事件中添加或删除。
因此,从 keypress
事件中删除的不是以前添加的keyPressListener
而是以前甚至没有添加的新创建的函数。最终结果是原始keyPressListener
保留添加到keypress
事件中。
解决方案 1
你移动keyPressListener
可以移出MyComp
。这是在keypress
事件中添加和删除时将引用的相同函数。
解决方案 2
您可以记住keyPressListener
函数,以便在keypress
事件中添加和删除相同的函数。由于您使用的是钩子,因此您可以继续使用useMemo或useCallback
const MyComp = ({ focused }) => {
const keyPressListener = ({ code }) => {
console.log('key press:', code);
};
const memoizedListener = useMemo(() => keyPressListener, []);
// or use
// const memoizedListener = useCallback(keyPressListener, []);
console.log('rendered again');
useEffect(() => {
if (focused) {
console.log('add event listener');
window.addEventListener('keydown', memoizedListener);
} else {
console.log('remove event listener');
window.removeEventListener('keydown', memoizedListener);
}
}, [focused, memoizedListener]);
return <h1>Test component</h1>;
};
希望这可以帮助您理解上述代码的工作原理。
在使用中附加 EventListener 效果钩子通常需要"清理"。像这样重写你的useEffect
钩子:
useEffect(() => {
if (focused) {
console.log('add event listener');
window.addEventListener('keydown', keyPressListener);
}
return () => {
console.log('remove event listener');
window.removeEventListener('keydown', keyPressListener);
};
}, [focused]);
您还可以使用useRef
钩子来包含要在清理时删除的事件侦听器回调。