reactjs 使用React Hooks在窗口上添加和删除mousemove侦听器

wgxvkvu9  于 2023-05-22  发布在  React
关注(0)|答案(1)|浏览(196)

我尝试在单击对象时在window上添加事件侦听器,然后在再次单击对象时删除该事件侦听器。
当单击Card组件时,状态isCardMoving被打开或关闭。
我添加了一个useEffect来观看isCardMoving。当isCardMoving被打开时,它应该向触发handleCardMove函数的窗口添加一个mousemove事件侦听器。这个函数只是记录鼠标的坐标。
如果我再次单击卡片,isCardMoving将为false,我希望窗口上的事件侦听器在useEffect中被删除。
但是,当isCardMovingtrue时,事件侦听器将被添加,而当isCardMovingfalse时,事件侦听器将不会被删除。

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)} />;
};

这似乎与以前的效果相同。

txu3uszq

txu3uszq1#

你不能在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", handleCardMove);
}, [isCardMoving, handleCardMove]);

工作演示:

相关问题