众所周知,事件监听器这种东西,除非是{once: true}
这种一次性监听器,否则凡注册的,必移除。
但是在React
的Hook
组件中,移除事件监听时却要留点神,否则可能代码写得自认为OK,实际一点效果都没有。
先来看个例子:
一个计数器,初始值为1,每点击一次按钮,数字+1,当当前数字为偶数时,注册监听器,为奇数时,移除监听器。
写法一
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [c, setC] = useState(1);
useEffect(() => {
if (c % 2 === 0) {
document.getElementById('root').addEventListener("keyup", (e) => {
console.log(e.key);
});
} else {
document.getElementById('root').removeEventListener("keyup", (e) => {
console.log(e.key);
});
}
}, [c]);
return (
<div className="App">
<h1>{c}</h1>
<button
onClick={(e) => {
setC(c + 1);
}}
>
点我加一
</button>
</div>
);
}
测试结果:
可以注册,无法移除。
写法二
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [c, setC] = useState(1);
useEffect(() => {
function f(e) {
console.log(e.key);
}
if (c % 2 === 0) {
document.getElementById("root").addEventListener("keyup", (e) => {
f(e);
});
} else {
document.getElementById("root").addEventListener("keyup", (e) => {
f(e);
});
}
}, [c]);
return (
<div className="App">
<h1>{c}</h1>
<button
onClick={(e) => {
setC(c + 1);
}}
>
点我加一
</button>
</div>
);
}
测试结果:
可以注册,无法移除。
写法三
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [c, setC] = useState(1);
useEffect(() => {
const f = (e) => {
console.log(e.key);
};
if (c % 2 === 0) {
document.getElementById("root").addEventListener("keyup", f);
} else {
document.getElementById("root").removeEventListener("keyup", f);
}
}, [c]);
return (
<div className="App">
<h1>{c}</h1>
<button
onClick={(e) => {
setC(c + 1);
}}
>
点我加一
</button>
</div>
);
}
测试结果:
可以注册,无法移除。
为什么 ?
一二失败很好理解,因为匿名函数不支持清除。
但是三为什么也失败?
因为组件随着数字的变化在不断地重新渲染。
数字每变换一次,组件就渲染一次,这样定义在组件内部的函数其内存也一直在变化。
所以,当数字为奇数时,被移除的监听器是一个从未被注册过的监听器。
而我们真正想要移除的那个监听器的内存已经不可获取了。
所以出现了只能注册,无法移除的现象。
解决
既然如此,那就只能在组件当前的这个生命周期内实现注册和移除。
import { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [c, setC] = useState(1);
useEffect(() => {
const f = (e) => {
console.log(e.key);
};
if (c % 2 === 0) {
document.getElementById("root").addEventListener("keyup", f);
}
return () => {
document.getElementById("root").removeEventListener("keyup", f);
};
}, [c]);
return (
<div className="App">
<h1>{c}</h1>
<button
onClick={(e) => {
setC(c + 1);
}}
>
点我加一
</button>
</div>
);
}
测试结果:
可以注册,可以移除。
总结
不要试图去移除一个回调函数为匿名函数的监听器
监听器的注册和移除不可跨越组件的生命周期
点击体验本文示例:
beCarefuleToremoveEventListener
相关文章
暂无评论...