javascript - Gatsby:事件监听器未在 React useEffects Hook 上卸载

标签 javascript reactjs react-hooks gatsby

我正在使用 Gatsby 。

我有这个 useEffect() Hook ,可以在文档中添加和删除事件监听器,以便我可以跟踪外部点击以关闭相应的菜单。但是当我导航到另一条路线并打开菜单时,应用程序崩溃并收到此错误:

header.js:121 Uncaught TypeError: Cannot read property 'contains' of null
    at handleDropdown (header.js:121)
    at HTMLDocument.<anonymous> (header.js:134)

由于我有三个事件监听器,因此每次单击任何地方都会出现三次此错误。我添加了一个控制台日志,并意识到在路由更改期间,refs 的值暂时变为空。我觉得问题是因为 header.js 卸载然后重新安装。但我添加了对 useEffect Hook 的返回,这应该删除事件监听器并在新路由加载时将它们添加回来。我上面做错了什么吗?

下面是我的函数和 useEffect Hook 。

// Function to close menus on outside clicks
  const handleDropdown = (menuRef, buttonRef, handler, e) => {
    console.log(!!menuRef.current, !!buttonRef.current)

    if (
      menuRef.current.contains(e.target) ||
      buttonRef.current.contains(e.target)
    ) {
      return
    }

    handler(false)
  }

  useEffect(() => {
    document.addEventListener("mousedown", e =>
      handleDropdown(mobileMenuRef, menuButtonRef, setShowMobileMenu, e)
    )

    document.addEventListener("mousedown", e =>
      handleDropdown(coursesMenuRef, coursesButtonRef, setShowCoursesMenu, e)
    )

    document.addEventListener("mousedown", e =>
      handleDropdown(
        schedulesMenuRef,
        schedulesButtonRef,
        setShowSchedulesMenu,
        e
      )
    )

    document.addEventListener("scroll", handleFixedNavbar)

    return () => {
      document.removeEventListener("mousedown", e =>
        handleDropdown(mobileMenuRef, menuButtonRef, setShowMobileMenu, e)
      )

      document.removeEventListener("mousedown", e =>
        handleDropdown(coursesMenuRef, coursesButtonRef, setShowCoursesMenu, e)
      )

      document.removeEventListener("mousedown", e =>
        handleDropdown(
          schedulesMenuRef,
          schedulesButtonRef,
          setShowSchedulesMenu,
          e
        )
      )

      document.removeEventListener("scroll", handleFixedNavbar)
    }
  }, [])

最佳答案

在 JavaScript 中,函数是通过引用进行比较的。这意味着 () => {} !== () => {}。当您尝试在 useEffect 清理回调中删除事件监听器时,您会传递新定义的函数,但由于上述原因,它们将与您定义的现有处理程序不匹配。

如果重构代码以将事件处理程序包含在 useEffect Hook 中,则可以将相同的 Hook 局部变量传递给两个 (add|remove)EventListener 调用:

const useOnClickOutside = (refs, callback) => {
  useEffect(() => {
    const eventHandler = e => {
      if (refs.some(ref => ref.current.contains(e.target))) {
        return
      } else {
        callback(false)
      }
    }

    document.addEventListener("mousedown", eventHandler)

    return () => {
      document.removeEventListener("mousedown", eventHandler)
    }
  }, [refs, callback])
}

const YourComponent = () => {
  const someRef = useRef()
  const someOtherRef = useRef()
  useOnClickOutside([someRef, someOtherRef], () => {
    console.log("Click outside happened!")
  })

  return <div>Some Content</div>
}

关于javascript - Gatsby:事件监听器未在 React useEffects Hook 上卸载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59569247/

相关文章:

javascript - 将日期从 JavaScript 转换为 Java

javascript - 使用 React 过滤对象列表

reactjs - 在带有 TypeScript 的 next.js 中使用 Router 的空安全方法

javascript - 在我的例子中如何从指令中获取值

javascript - 显示像 redux devTool 这样的 React reducer 操作

reactjs - 选定的值未显示在文本字段选择中 - Material UI react 组件

javascript - 已内存组件的子组件是否也已内存?

javascript - React Hooks 实现计数器

javascript - React中如何用hooks实现componentDidMount才能符合EsLint规则 "react-hooks/exhaustive-deps": "warn"?

javascript - 使复选框成为一组单选按钮