我如何使用React useEffect窗口removeEventListener

时间:2019-03-26 15:23:28

标签: reactjs react-hooks removeeventlistener

在React Hooks文档中,显示了如何在组件的清理阶段删除removeEventListener。 https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect

在我的用例中,我试图删除以功能组件的state属性为条件的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 />&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')));

(位于https://codepen.io/caqu/pen/xBeBMN的实时示例)

我看到的问题是,handleKeyUp内的removeEventListener引用每次在组件呈现时都会改变。函数handleKeyUp需要引用setCollapsed,因此必须用App括起来。将handleKeyUp移入useEffect似乎也多次触发,并且失去了对原始handleKeyUp的引用。

如何使用React Hooks有条件地window.removeEventListener而不卸载组件?

2 个答案:

答案 0 :(得分:3)

您可以将handleKeyUp函数放在给useEffect的函数中,并且仅添加侦听器,并在collapsed为假时返回清除函数。

useEffect(() => {
  if (!collapsed) {
    function handleKeyUp(event) {
      switch (event.key) {
        case "Escape":
          setCollapsed(true);
          break;
      }
    }

    window.addEventListener("keyup", handleKeyUp);
    return () => window.removeEventListener("keyup", handleKeyUp);
  }
}, [collapsed]);

答案 1 :(得分:3)

Tholle的答案可能有用,但是在if内声明一个函数是不好的做法。声明函数时和不声明函数时,都很难遵循。另外,由于函数被提升,也可能导致错误。
有一种更整洁的方法可以解决它:

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

const [collapsed, setCollapsed] = useState(true)

const handleKeyUp = useCallback(() => {
    switch (event.key) {
        case "Escape":
            setCollapsed(true)
            break
    }
}, [setCollapsed])

useEffect(() => {
    window.addEventListener("keyup", handleKeyUp)
    // See note below
    return () => window.removeEventListener("keyup", handleKeyUp)
  }
}, [collapsed])

如果您在useEffect中使用了大量事件处理程序,则有一个自定义的钩子:https://usehooks.com/useEventListener/

注意:

由于您正在听keyup,因此您甚至可能想在collapsed === true时删除事件侦听器,因为没有任何东西可以折叠。否则,只要安装了组件,事件就会一直触发。

useEffect(() => {
    if (!collapsed) {
        window.addEventListener("keyup", handleKeyUp)
    } else {
        window.removeEventListener("keyup", handleKeyUp)
    }

    return () => window.removeEventListener("keyup", handleKeyUp)
  }
}, [collapsed]);