React Hooks:如何清理事件监听器?
React Hooks: How to clean event listeners?
我有一个组件 Example
,当本地状态 display
变为 false[ 时,我想在其中清除 事件侦听器 当 show
属性为 true 时,=43=],display
设置为 true,反之亦然。
代码:
const Example = ({ show, onClose }) => {
const [display, setDisplay] = useState(false);
const handleClick = (e) => {
onClose();
console.log(display);
};
useEffect(() => {
if (display) {
document.addEventListener('click', handleClick);
} else {
document.removeEventListener('click', handleClick);
}
}, [display, handleClick]);
useEffect(() => {
if (show) {
setDisplay(show);
} else {
setDisplay(false);
}
}, [show]);
return <div>{display && <p>Hi</p>}</div>;
};
问题:
- 组件无法清除事件侦听器,因为每次都会对函数
handleClick()
进行 新引用 Example
渲染。
- 每次调用
handleClick()
时,它都会将 display
记录为 true。
- 7-8 次鼠标点击后,控制台的日志 count 达到 7-8K.
我在这里做错了什么?谢谢:)
为了清理一个useEffect,return一个effect中的函数。每次重新运行效果和卸载组件时都会调用该函数。
这将允许正确删除事件侦听器的原因是因为 useEffect 在 handleClick
函数的当前版本上创建了一个闭包。这允许清理函数具有相同的引用,因此可以正确清理它。它在原始版本中不起作用,因为每次 useEffect 重新 运行 时,都会关闭 handleClick
的新版本,然后尝试使用新版本进行清理。
useEffect(() => {
if (!display) {
return;
}
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
}, [display, handleClick]);
您可以通过使用对 handleClick
函数的引用来进一步降低这种效果的发生频率。
例如,这是最基本的。尽管您可以轻松地将一些参考和额外的使用效果抽象为一个单独的挂钩。
const handleClickRef = useRef(handleClick);
useEffect(()=>{
handleClickRef.current = handleClick;
},[handleClick])
useEffect(() => {
if (!display) {
return;
}
const funct = (evt)=>handleClickRef.current(evt);
document.addEventListener('click',funct);
return () => document.removeEventListener('click', funct);
}, [display]);
我有一个组件 Example
,当本地状态 display
变为 false[ 时,我想在其中清除 事件侦听器 当 show
属性为 true 时,=43=],display
设置为 true,反之亦然。
代码:
const Example = ({ show, onClose }) => {
const [display, setDisplay] = useState(false);
const handleClick = (e) => {
onClose();
console.log(display);
};
useEffect(() => {
if (display) {
document.addEventListener('click', handleClick);
} else {
document.removeEventListener('click', handleClick);
}
}, [display, handleClick]);
useEffect(() => {
if (show) {
setDisplay(show);
} else {
setDisplay(false);
}
}, [show]);
return <div>{display && <p>Hi</p>}</div>;
};
问题:
- 组件无法清除事件侦听器,因为每次都会对函数
handleClick()
进行 新引用Example
渲染。 - 每次调用
handleClick()
时,它都会将display
记录为 true。 - 7-8 次鼠标点击后,控制台的日志 count 达到 7-8K.
我在这里做错了什么?谢谢:)
为了清理一个useEffect,return一个effect中的函数。每次重新运行效果和卸载组件时都会调用该函数。
这将允许正确删除事件侦听器的原因是因为 useEffect 在 handleClick
函数的当前版本上创建了一个闭包。这允许清理函数具有相同的引用,因此可以正确清理它。它在原始版本中不起作用,因为每次 useEffect 重新 运行 时,都会关闭 handleClick
的新版本,然后尝试使用新版本进行清理。
useEffect(() => {
if (!display) {
return;
}
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
}, [display, handleClick]);
您可以通过使用对 handleClick
函数的引用来进一步降低这种效果的发生频率。
例如,这是最基本的。尽管您可以轻松地将一些参考和额外的使用效果抽象为一个单独的挂钩。
const handleClickRef = useRef(handleClick);
useEffect(()=>{
handleClickRef.current = handleClick;
},[handleClick])
useEffect(() => {
if (!display) {
return;
}
const funct = (evt)=>handleClickRef.current(evt);
document.addEventListener('click',funct);
return () => document.removeEventListener('click', funct);
}, [display]);