问题描述
我试图在单击对象时在window
上添加事件侦听器,然后在再次单击对象时删除该事件侦听器。
单击Card
组件时,状态isCardMoving
处于打开或关闭状态。
我添加了一个useEffect
来观看isCardMoving
。启用isCardMoving
时,应在触发mousemove
函数的窗口中添加一个handleCardMove
事件监听器。此功能仅记录鼠标的坐标。
如果再次单击该卡,则isCardMoving
将为假,并且我希望窗口上的事件侦听器将在useEffect
内被删除。
但是,发生的事情是,事件监听器将在isCardMoving
为true
时被添加,而一旦isCardMoving
为false
时将不会被删除。
import React from 'react';
const App = () => {
const [isCardMoving,setIsCardMoving] = React.useState(false);
React.useEffect(() => {
if (isCardMoving) window.addEventListener('mousemove',handleCardMove);
else window.removeEventListener('mousemove',handleCardMove);
},[isCardMoving]);
const handleCardMove = (event) => console.log({ x: event.offsetX,y: event.offsetY });
return <Card onClick={() => setIsCardMoving(!isCardMoving)} />;
};
然后我试图在窗口上设置ref
,以为可能出于某种原因我可能需要对该窗口的先前引用:
import React from 'react';
const App = () => {
const [isCardMoving,setIsCardMoving] = React.useState(false);
const windowRef = React.useRef(window); // add window ref
// update window ref whenever window is updated
React.useEffect(() => {
windowRef.current = window;
},[window]);
React.useEffect(() => {
// add and remove event listeners on windowRef
if (isCardMoving) windowRef.current.addEventListener('mousemove',handleCardMove);
else windowRef.current.removeEventListener('mousemove',y: event.offsetY });
return <Card onClick={() => setIsCardMoving(!isCardMoving)} />;
};
这似乎和以前一样。
解决方法
实际上,您不能在React或任何其他基于虚拟DOM的应用程序中删除这样的事件侦听器。由于虚拟DOM库的性质,您必须在卸载生命周期中删除事件侦听器,该生命周期位于React钩子中,可在useEffect
本身中使用。因此,您必须像下面那样使用return关键字来执行此操作,它将与类基础组件中的componentWillUnmount
相同:
React.useEffect(() => {
if (isCardMoving) window.addEventListener("mousemove",handleCardMove);
return () => window.removeEventListener("mousemove",handleCardMove);
},[isCardMoving]);
工作演示:
更新
正如@ZacharyHaber在评论中所说,此行为背后的主要原因是因为handleCardMove
函数将在每个渲染器上重新定义,因此,要克服这种情况,我们需要使用以下方法在每个渲染器上将事件与窗口解除绑定useEffect
回调。您还可以使用useCallback
方法来使初始代码正常工作,但是还需要向组件中添加先前的useEffect
回调,以确保事件监听器将在组件卸载周期中删除,这有点更多的编码,但这将与上述方法相同。
const handleCardMove = React.useCallback((event) => {
console.log({ x: event.offsetX,y: event.offsetY });
},[]);
React.useEffect(() => {
if (isCardMoving) window.addEventListener("mousemove",handleCardMove);
else window.removeEventListener("mousemove",handleCardMove);
return () => window.removeEventListener("mousemove",[isCardMoving,handleCardMove]);
工作演示: