我尝试在单击对象时在window
上添加事件侦听器,然后在再次单击对象时删除该事件侦听器。
当单击Card
组件时,状态isCardMoving
被打开或关闭。
我添加了一个useEffect
来观看isCardMoving
。当isCardMoving
被打开时,它应该向触发handleCardMove
函数的窗口添加一个mousemove
事件侦听器。这个函数只是记录鼠标的坐标。
如果我再次单击卡片,isCardMoving
将为false,我希望窗口上的事件侦听器在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', handleCardMove);
}, [isCardMoving]);
const handleCardMove = (event) => console.log({ x: event.offsetX, y: event.offsetY });
return <Card onClick={() => setIsCardMoving(!isCardMoving)} />;
};
这似乎与以前的效果相同。
1条答案
按热度按时间txu3uszq1#
你不能在React或其他基于DOM的虚拟应用程序中删除这样的事件侦听器。由于虚拟DOM库的性质,您必须在卸载生命周期中删除事件侦听器,该侦听器位于React钩子中,并且在
useEffect
本身中可用。所以你必须像下面这样使用return关键字。它将做与类基础组件中的componentWillUnmount
相同的事情:工作演示:
更新
正如@ZacharyHaber在评论中所说,这种行为背后的主要原因是你的
handleCardMove
函数将在每次渲染时被重新定义,所以为了克服这种情况,我们需要在每次渲染时使用useEffect
回调从窗口中解除绑定事件。您还可以使用useCallback
方法使初始代码工作。尽管如此,您还需要将前面的useEffect
回调添加到您的组件中,以确保事件侦听器将在组件卸载周期中删除,这是一个更多的编码,但这个方法与上面的方法相同。工作演示: