我不能删除eventListener
,即使使用React.useCallback
也不行。那么,我现在该怎么办?
下面是我的代码,位于组件类的开头:
const noCursorEventListener = React.useCallback((e) => {
console.log('ncel');
let lista = document.getElementsByClassName('lista');
if (lista && lista[0]) lista[0].classList.remove('nocursor');
}, []);
window.addEventListener('mousemove', noCursorEventListener);
用于删除它的useEffect:
useEffect(() => {
return () => {
window.removeEventListener('mousemove', noCursorEventListener);
window.onmousemove = null;
console.log('remove el');
}
});
我看到的remove el
是正确的,但是在那之后,在页面更改之后,我仍然收到ncel
消息。我该如何修复它?
那个window.onmousemove = null
不应该是必要的,它是一个失败的测试。
1条答案
按热度按时间c86crjj01#
TL;DR不要在render中做(非钩子)副作用。:-)对于潜伏者,如果你只是想知道如何使用DOM方法正确地添加事件侦听器(在极少数情况下是合适的),请参阅下面的标准方法。
但对于那些对OP的代码为什么不起作用感兴趣的人来说:
为什么不起作用
您所拥有的将适用于 * 第一次 * 渲染,但不适用于后续渲染。(如果您使用React的
StrictMode
,它可能在开始时渲染了两次。) 您可以看到为什么我们在发生的每个阶段都记录一条消息(我将mousemove
更改为click
,因为这无关紧要,而且避免了日志混乱):如果运行该命令,您将看到
Adding event listener
,因为渲染器添加了事件侦听器。如果单击按钮以外的其他位置,您将看到event listener called!
。但如果单击按钮进行第二次渲染,您将看到以下序列:注意顺序。它重新添加了事件侦听器(这不做任何事情,因为你不能为同一个事件向同一个元素添加同一个事件侦听器函数超过一次),然后在渲染后为 * 前一次 * 渲染运行
useEffect
清理,删除事件侦听器。这在useEffect
清理的工作方式中是隐含的,但看起来有点令人惊讶。有趣的是,如果你没有记住事件侦听器,它会工作,因为当添加时,它会短暂地添加第二个事件侦听器,然后第一个会被
useEffect
清理删除。一个三个三个一个
但是不要这样做。除了调用钩子,你的
render
函数应该是纯的(它不应该有有有意义的副作用)。添加一个事件监听器是一个有意义的副作用。副作用是
useEffect
(more here)的关键所在,所以让我们用标准的方法来做,在useEffect
回调函数中连接侦听器,并在清除该效果时删除同一个处理程序(这也意味着我们不必在每次丢弃侦听器函数时都创建一个新的侦听器函数)。标准方式
下面是在挂载时添加事件侦听器并在卸载时删除它的标准方法,适用于那些相对较少的用例,直接使用DOM执行此操作比较合适:
(还有一个单独的问题:
useCallback
是 * 性能优化 ,而不是语义保证。useCallback
是useMemo
的 Package 器,它有以下免责声明 (强调):"您可能依赖useMemo作为性能优化,而不是作为语义保证。"* 但您的代码依赖它作为语义保证。)