如何删除 React 组件中的 window eventListener?
How to remove window eventListener in React component?
这段代码显示了用户鼠标坐标,它工作正常。
但我还想通过单击“停止”按钮删除事件处理程序。不幸的是,这似乎是错误的,因为即使在单击按钮后,事件处理程序仍在跟踪和更改 position:
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>
)
}
我猜 context 可能有问题,但我不确定如何解决它。
顺便说一句,这是 React 组件,我在 App.js 中使用它是这样的:
import MouseCords from './components/MouseCords'
function App() {
return (
<div>
<MouseCords />
</div>
)
}
export default App
useEffect(() => {
window.addEventListener('mousemove', mouseMoveHandler)
return window.removeEventListener('mousemove', mouseMoveHandler)
}, [])
在组件顶层声明的函数将在每次渲染时重新声明,按钮 onClick
属性 中的匿名函数也是如此。因此,当您尝试删除侦听器时,点击处理程序中的函数引用将与设置侦听器的函数引用不匹配,因此它不会起作用。
最简单的解决方案是使用 useCallback
声明函数,这将确保函数仅在第一次渲染时声明(由于依赖项数组为空),因此点击处理程序和 useEffect 函数引用将匹配.
您还应该始终通过从 useEffect
返回一个函数来提供一个 'cleanup' function ,该函数将在卸载时清理任何 side-effects 我在组件生命周期中引起的 useEffect .
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>
或者,您可以完全通过 useEffect
处理侦听器,并在每次更改时使用布尔标志有条件地 add/remove 侦听器。在这种情况下,每次依赖数组发生变化时,'cleanup' 将 运行。
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>
这段代码显示了用户鼠标坐标,它工作正常。
但我还想通过单击“停止”按钮删除事件处理程序。不幸的是,这似乎是错误的,因为即使在单击按钮后,事件处理程序仍在跟踪和更改 position:
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>
)
}
我猜 context 可能有问题,但我不确定如何解决它。
顺便说一句,这是 React 组件,我在 App.js 中使用它是这样的:
import MouseCords from './components/MouseCords'
function App() {
return (
<div>
<MouseCords />
</div>
)
}
export default App
useEffect(() => {
window.addEventListener('mousemove', mouseMoveHandler)
return window.removeEventListener('mousemove', mouseMoveHandler)
}, [])
在组件顶层声明的函数将在每次渲染时重新声明,按钮 onClick
属性 中的匿名函数也是如此。因此,当您尝试删除侦听器时,点击处理程序中的函数引用将与设置侦听器的函数引用不匹配,因此它不会起作用。
最简单的解决方案是使用 useCallback
声明函数,这将确保函数仅在第一次渲染时声明(由于依赖项数组为空),因此点击处理程序和 useEffect 函数引用将匹配.
您还应该始终通过从 useEffect
返回一个函数来提供一个 'cleanup' function ,该函数将在卸载时清理任何 side-effects 我在组件生命周期中引起的 useEffect .
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>
或者,您可以完全通过 useEffect
处理侦听器,并在每次更改时使用布尔标志有条件地 add/remove 侦听器。在这种情况下,每次依赖数组发生变化时,'cleanup' 将 运行。
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>