我如何 window removeEventListener 使用 React useEffect
How do I window removeEventListener using React useEffect
在 React Hooks 文档中展示了如何在组件的清理阶段移除 EventListener。 https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
在我的用例中,我尝试以功能组件的状态 属性 为条件移除 EventListener。
这是一个永远不会卸载组件但应该删除事件侦听器的示例:
function App () {
const [collapsed, setCollapsed] = React.useState(true);
React.useEffect(
() => {
if (collapsed) {
window.removeEventListener('keyup', handleKeyUp); // Not the same "handleKeyUp" :(
} else {
window.addEventListener('keyup', handleKeyUp);
}
},
[collapsed]
);
function handleKeyUp(event) {
console.log(event.key);
switch (event.key) {
case 'Escape':
setCollapsed(true);
break;
}
}
return collapsed ? (
<a href="javascript:;" onClick={()=>setCollapsed(false)}>Search</a>
) : (
<span>
<input placeholder="Search" autoFocus />
<a href="javascript:;">This</a>
<a href="javascript:;">That</a>
<input placeholder="Refinement" />
</span>
);
}
ReactDOM.render(<App />, document.body.appendChild(document.createElement('div')));
(现场样本在 https://codepen.io/caqu/pen/xBeBMN)
我看到的问题是 removeEventListener
中的 handleKeyUp
引用在每次组件呈现时都会发生变化。函数 handleKeyUp
需要对 setCollapsed
的引用,因此它必须包含在 App
中。将 handleKeyUp
移入 useEffect
似乎也会触发多次并丢失对原始 handleKeyUp
.
的引用
如何在不卸载组件的情况下有条件地 window.removeEventListener 使用 React Hooks?
您可以将 handleKeyUp
函数放在给定 useEffect
(which is the recommended way of doing it according to the official documentation) 的函数中,并且只添加侦听器和 return 当 collapsed
是错误的。
useEffect(() => {
if (collapsed) {
return;
}
function handleKeyUp(event) {
switch (event.key) {
case "Escape":
setCollapsed(true);
break;
}
}
window.addEventListener("keyup", handleKeyUp);
return () => window.removeEventListener("keyup", handleKeyUp);
}, [collapsed]);
Tholle 的 答案可能有效,但在 if
.
中声明函数是不好的做法
函数声明和未声明的时间更难理解。它还可能导致错误,因为函数被提升了。
有更简洁的方法来修复它:
通过使用 useCallback 挂钩包装您的事件处理程序。
const [collapsed, setCollapsed] = useState(true)
const handleKeyUp = useCallback((event) => {
if (event.key === "Escape") {
setCollapsed(true)
}
}, [setCollapsed])
useEffect(() => {
if (!collapsed) {
window.addEventListener("keyup", handleKeyUp)
} else {
window.removeEventListener("keyup", handleKeyUp)
}
return () => window.removeEventListener("keyup", handleKeyUp)
}, [collapsed, handleKeyUp])
useCallback
依赖于 setCollapsed
。这确保 handleKeyUp
在组件重新呈现时不会被重新定义(这总是在状态更改时发生)
useEffect
将有条件地 add/remove 事件侦听器,否则只要安装了组件,事件就会一直触发。
If you use a lot of event handlers in useEffect, there's a custom hook for that: https://usehooks.com/useEventListener/
这是使用我的解决方案更新的问题海报示例:https://codepen.io/publicJorn/pen/eYzwENN
在 React Hooks 文档中展示了如何在组件的清理阶段移除 EventListener。 https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect
在我的用例中,我尝试以功能组件的状态 属性 为条件移除 EventListener。
这是一个永远不会卸载组件但应该删除事件侦听器的示例:
function App () {
const [collapsed, setCollapsed] = React.useState(true);
React.useEffect(
() => {
if (collapsed) {
window.removeEventListener('keyup', handleKeyUp); // Not the same "handleKeyUp" :(
} else {
window.addEventListener('keyup', handleKeyUp);
}
},
[collapsed]
);
function handleKeyUp(event) {
console.log(event.key);
switch (event.key) {
case 'Escape':
setCollapsed(true);
break;
}
}
return collapsed ? (
<a href="javascript:;" onClick={()=>setCollapsed(false)}>Search</a>
) : (
<span>
<input placeholder="Search" autoFocus />
<a href="javascript:;">This</a>
<a href="javascript:;">That</a>
<input placeholder="Refinement" />
</span>
);
}
ReactDOM.render(<App />, document.body.appendChild(document.createElement('div')));
(现场样本在 https://codepen.io/caqu/pen/xBeBMN)
我看到的问题是 removeEventListener
中的 handleKeyUp
引用在每次组件呈现时都会发生变化。函数 handleKeyUp
需要对 setCollapsed
的引用,因此它必须包含在 App
中。将 handleKeyUp
移入 useEffect
似乎也会触发多次并丢失对原始 handleKeyUp
.
如何在不卸载组件的情况下有条件地 window.removeEventListener 使用 React Hooks?
您可以将 handleKeyUp
函数放在给定 useEffect
(which is the recommended way of doing it according to the official documentation) 的函数中,并且只添加侦听器和 return 当 collapsed
是错误的。
useEffect(() => {
if (collapsed) {
return;
}
function handleKeyUp(event) {
switch (event.key) {
case "Escape":
setCollapsed(true);
break;
}
}
window.addEventListener("keyup", handleKeyUp);
return () => window.removeEventListener("keyup", handleKeyUp);
}, [collapsed]);
Tholle 的 答案可能有效,但在 if
.
函数声明和未声明的时间更难理解。它还可能导致错误,因为函数被提升了。
有更简洁的方法来修复它:
通过使用 useCallback 挂钩包装您的事件处理程序。
const [collapsed, setCollapsed] = useState(true)
const handleKeyUp = useCallback((event) => {
if (event.key === "Escape") {
setCollapsed(true)
}
}, [setCollapsed])
useEffect(() => {
if (!collapsed) {
window.addEventListener("keyup", handleKeyUp)
} else {
window.removeEventListener("keyup", handleKeyUp)
}
return () => window.removeEventListener("keyup", handleKeyUp)
}, [collapsed, handleKeyUp])
useCallback
依赖于setCollapsed
。这确保handleKeyUp
在组件重新呈现时不会被重新定义(这总是在状态更改时发生)useEffect
将有条件地 add/remove 事件侦听器,否则只要安装了组件,事件就会一直触发。
If you use a lot of event handlers in useEffect, there's a custom hook for that: https://usehooks.com/useEventListener/
这是使用我的解决方案更新的问题海报示例:https://codepen.io/publicJorn/pen/eYzwENN