如何删除React组件中的窗口EventListener?

发布于 2025-01-28 12:01:59 字数 1231 浏览 3 评论 0原文

代码的平静显示用户鼠标坐标,它可以正常工作。

但是我也想通过单击停止按钮来删除事件处理程序。不幸的是,这似乎是错误的,因为即使在单击按钮后,事件处理程序仍在跟踪和更改位置

import { useEffect, useState } from 'react'

function MouseCords() {
    const [position, setPosition] = useState({ x: 0, y: 0 })

    function mouseMoveHandler(event) {
        setPosition({
            x: event.clientX,
            y: event.clientY
        })
    }

    useEffect(() => {
        window.addEventListener('mousemove', mouseMoveHandler)
    }, [])

    return (
        <div>
            <pre>
                x: {position.x}
                <br />
                y: {position.y}
            </pre>
            <button
                onClick={() =>
                    window.removeEventListener('mousemove', mouseMoveHandler)
                }
            >
                STOP
            </button>
        </div>
    )
}

我想上下文可能存在问题,但我不是当然该怎么办来解决它。

顺便说一句,这是React组件,我在App.js中使用它:

import MouseCords from './components/MouseCords'

function App() {
    return (
        <div>
            <MouseCords />
        </div>
    )
}

export default App

This peace of code shows user mouse coordinates, it works properly.

But I also want to remove event handler by clicking the STOP button. Unfortunately, this seems to be wrong somehow because event handler still tracking and changing position even after clicking the button:

import { useEffect, useState } from 'react'

function MouseCords() {
    const [position, setPosition] = useState({ x: 0, y: 0 })

    function mouseMoveHandler(event) {
        setPosition({
            x: event.clientX,
            y: event.clientY
        })
    }

    useEffect(() => {
        window.addEventListener('mousemove', mouseMoveHandler)
    }, [])

    return (
        <div>
            <pre>
                x: {position.x}
                <br />
                y: {position.y}
            </pre>
            <button
                onClick={() =>
                    window.removeEventListener('mousemove', mouseMoveHandler)
                }
            >
                STOP
            </button>
        </div>
    )
}

I guess there might be a problem with context, but I am not sure what to do to solve it.

By the way, this is React component and I am using it in App.js like this:

import MouseCords from './components/MouseCords'

function App() {
    return (
        <div>
            <MouseCords />
        </div>
    )
}

export default App

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

蔚蓝源自深海 2025-02-04 12:01:59

在每个渲染上都会重新计算在组件的最高级别上声明的函数,按钮的onclick onclick属性中的匿名函数也将被重新启动。因此,当您尝试删除侦听器时,点击处理程序中的函数引用与侦听器所设置的函数参考不匹配,因此它无法正常工作。

最简单的解决方案是使用 usecallback这将确保仅在第一个渲染时声明该函数(由于空依赖性数组),因此单击处理程序和使用效应函数引用将匹配。

您还应该始终提供'nofollow noreferrer“通过从您的使用中返回一个函数,该函数将清除我在组件的生命周期中引起的任何副作用的副作用。

const { useState, useEffect, useCallback } = React;

function MouseCoords() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const mouseMoveHandler = useCallback((event) => {
    setPosition({
      x: event.clientX,
      y: event.clientY
    });
  }, []);

  useEffect(() => {
    window.addEventListener('mousemove', mouseMoveHandler);
    
    return () => {
      window.removeEventListener('mousemove', mouseMoveHandler);
    };
  }, []);

  return (
    <div>
      <pre>
        x: {position.x}
        <br />
        y: {position.y}
      </pre>
      <button
        onClick={() =>
          window.removeEventListener('mousemove', mouseMoveHandler)
        }
      >
        STOP
      </button>
    </div>
  )
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MouseCoords />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id='root'></div>

另外,您可以通过使用效果完全处理侦听器,并在每次更改时都使用布尔标志来有条件地添加/删除侦听器。在这种情况下,每次依赖关系数组都会发生变化时,“清理”将运行。

const { useState, useEffect, useCallback } = React;

function MouseCoords() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [shouldTrack, setShouldTrack] = useState(true);

  const mouseMoveHandler = useCallback((event) => {
    setPosition({
      x: event.clientX,
      y: event.clientY
    });
  }, []);

  useEffect(() => {
    if (shouldTrack) {
      window.addEventListener('mousemove', mouseMoveHandler);
    
      return () => {
        window.removeEventListener('mousemove', mouseMoveHandler);
      };
    }
  }, [shouldTrack]);

  return (
    <div>
      <pre>
        x: {position.x}
        <br />
        y: {position.y}
      </pre>
      <button
        onClick={() => setShouldTrack(b => !b)}
      >
        {shouldTrack ? 'Stop' : 'Start'}
      </button>
    </div>
  )
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MouseCoords />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id='root'></div>

A function declared at the top level of a component will be redeclared on every render, as will the anonymous function in the button's onClick property. As such, when you try to remove the listener the function reference in the click handler won't match the function reference that the listener was set with and so it won't work.

The simplest solution is to declare the function using useCallback which will ensure that the function is only declared on first render (due to the empty dependency array) and so the click handler and useEffect function references will match.

You should also always provide a 'cleanup' function by returning a function from your useEffect that will clean up on unmount any side-effects the useEffect my have caused during the lifecycle of the component.

const { useState, useEffect, useCallback } = React;

function MouseCoords() {
  const [position, setPosition] = useState({ x: 0, y: 0 });

  const mouseMoveHandler = useCallback((event) => {
    setPosition({
      x: event.clientX,
      y: event.clientY
    });
  }, []);

  useEffect(() => {
    window.addEventListener('mousemove', mouseMoveHandler);
    
    return () => {
      window.removeEventListener('mousemove', mouseMoveHandler);
    };
  }, []);

  return (
    <div>
      <pre>
        x: {position.x}
        <br />
        y: {position.y}
      </pre>
      <button
        onClick={() =>
          window.removeEventListener('mousemove', mouseMoveHandler)
        }
      >
        STOP
      </button>
    </div>
  )
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MouseCoords />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id='root'></div>

Alternatively you could handle the listener entirely through the useEffect and use a boolean flag to conditionally add/remove the listener everytime it changes. In this case the 'cleanup' will run each time there is a change in the dependency array.

const { useState, useEffect, useCallback } = React;

function MouseCoords() {
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [shouldTrack, setShouldTrack] = useState(true);

  const mouseMoveHandler = useCallback((event) => {
    setPosition({
      x: event.clientX,
      y: event.clientY
    });
  }, []);

  useEffect(() => {
    if (shouldTrack) {
      window.addEventListener('mousemove', mouseMoveHandler);
    
      return () => {
        window.removeEventListener('mousemove', mouseMoveHandler);
      };
    }
  }, [shouldTrack]);

  return (
    <div>
      <pre>
        x: {position.x}
        <br />
        y: {position.y}
      </pre>
      <button
        onClick={() => setShouldTrack(b => !b)}
      >
        {shouldTrack ? 'Stop' : 'Start'}
      </button>
    </div>
  )
}

const container = document.getElementById('root');
const root = ReactDOM.createRoot(container);
root.render(<MouseCoords />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>

<div id='root'></div>

无所谓啦 2025-02-04 12:01:59
useEffect(() => {
        window.addEventListener('mousemove', mouseMoveHandler)

return window.removeEventListener('mousemove', mouseMoveHandler)
    }, [])
useEffect(() => {
        window.addEventListener('mousemove', mouseMoveHandler)

return window.removeEventListener('mousemove', mouseMoveHandler)
    }, [])
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文