Reactjs 如何使用React useEffect删除Windows EventListener

Reactjs 如何使用React useEffect删除Windows EventListener,reactjs,react-hooks,removeeventlistener,Reactjs,React Hooks,Removeeventlistener,在React Hooks文档中,演示了如何在组件的清理阶段移除EventListener 在我的用例中,我试图将VentListener条件删除为功能组件的状态属性 下面是一个从未卸载组件但应移除事件侦听器的示例: function App () { const [collapsed, setCollapsed] = React.useState(true); React.useEffect( () => { if (collapsed) {

在React Hooks文档中,演示了如何在组件的清理阶段移除EventListener

在我的用例中,我试图将VentListener条件删除为功能组件的状态属性

下面是一个从未卸载组件但应移除事件侦听器的示例:

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 />&nbsp;
      <a href="javascript:;">This</a>&nbsp;
      <a href="javascript:;">That</a>&nbsp;
      <input placeholder="Refinement" />
    </span>
  );
}
ReactDOM.render(<App />, document.body.appendChild(document.createElement('div')));
函数应用程序(){
const[collapsed,setCollapsed]=React.useState(true);
反作用(
() => {
如果(折叠){
window.removeEventListener('keyup',handleKeyUp);//不同的“handleKeyUp”:(
}否则{
window.addEventListener('keyup',handleKeyUp);
}
},
[崩溃]
);
函数handleKeyUp(事件){
console.log(event.key);
开关(事件键){
“逃跑”一案:
设置崩溃(真);
打破
}
}
返回崩溃(
) : (
);
}
ReactDOM.render()

我看到的问题是
handleKeyUp
引用在
removeEventListener
中每次组件呈现时都在更改。函数
handleKeyUp
需要引用
setCollapsed
,因此它必须被
App
包围。在
useffect
中移动
handleKeyUp
似乎多次开火并丢失对原始
handleKeyUp
的引用


如何在不卸载组件的情况下使用React钩子有条件地window.removeEventListener?

您可以将
handleKeyUp
函数放在指定给
useffect
()的函数中,并且仅在
collapsed
为false时添加侦听器并返回清理函数

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]);
Thole的答案可能有效,但在
if
中声明函数是一种不好的做法

当函数被声明时和未声明时,这会使跟踪变得更加困难。此外,由于函数被挂起,这也会导致错误

有一种更简洁的方法来修复它:

通过用钩子包装事件处理程序

  • useCallback
    依赖于
    setCollapsed
    。这确保在组件重新加载时不会重新定义
    handleKeyUp
    (状态更改时总是发生这种情况)
  • useffect
    将有条件地添加/删除事件侦听器,否则只要安装了组件,事件就会一直触发
如果在useEffect中使用了大量事件处理程序,则会有一个自定义挂钩:


下面是用我的解决方案更新的问题海报示例:

非常有用!看起来你的
useffect
中有额外的收尾花括号。这正是我想要的,非常感谢@publicJorn!为什么要将
setCollapsed
作为
useCallback
的参数?这不是等价的吗只要做
[]
?在上面的例子中,什么定义了
事件
?@publicjorn实际上Thole的答案更好,如果可能的话。尽可能在useEffect中定义回调。那么你就不必为useCallback操心了。正如Kent C Dodds所建议的:如果必须为要调用的效果定义函数,请在效果回调内部执行,而不是在外部执行
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])