React - 我的自定义钩子在元素外点击有什么问题?

时间:2021-01-03 19:12:57

标签: reactjs

我有一个自定义钩子,用于在菜单中的元素外单击。发生的情况是:当我点击“打开菜单”时,它会打开菜单,当我在外面点击它时会关闭它(一切都很好)但是当我再次点击“打开菜单”按钮关闭它时(打开它后),它将其关闭一秒钟,然后再次打开。我一直在寻找试图找到原因但不能。任何帮助都会很棒。对不起,如果我没有很好地表达我的问题,我不知道如何问得更清楚:)谢谢!!

const { useState, useRef } = React;

const App = () => {
  const [showMenu, setShowMenu] = React.useState(false)

  return (
    <div>
      <div className="menu-section-conatainer">
        <button
          className={"menu-link"}
          onClick={() => {
            setShowMenu(true)
          }}
          name={title}
        >
          open menu
        </button>
      </div>

      <Menu
        showState={showMenu}
        clickHandler={setShowMenu}
      >
        {children}
      </Menu>
    </div>
  )
}

const Menu = ({ children, clickHandler, showState}) => {
  const ref = useRef()
  useOnClickOutside(ref, () => clickHandler(false))

  return (
    <div
      className="menu-expanded-wrapper"
    >
      <div ref={ref} className="menu-expanded">
        {children}
      </div>
    </div>
  )
}

const useOnClickOutside = (ref, handler) => {
  React.useEffect(() => {
    const listener = event => {
      if (!ref.current || ref.current.contains(event.target)) {
        return
      }
      handler(event)
    }

    document.addEventListener("mousedown", listener)
    document.addEventListener("touchstart", listener)

    return () => {
      document.removeEventListener("mousedown", listener)
      document.removeEventListener("touchstart", listener)
    }
  }, [ref, handler])
}

1 个答案:

答案 0 :(得分:1)

在菜单已经打开的情况下单击按钮时,您的菜单组件关闭然后立即再次打开的原因是,当您单击按钮时,useOnClickOutside 中的侦听器被调用(并关闭菜单),然后 {调用按钮上的 {1}} 函数(并打开菜单)。

您可以通过将 onClick 的使用移至 useOnClickOutside 组件并将引用放在作为按钮和菜单的父元素的元素上来解决此问题,以便忽略对按钮的点击出于“外”的目的。此外,这可能是一种更好的结构,因此打开/关闭逻辑仅包含在 App 组件中。

然后您应该更新按钮的 App 函数,使其切换 onClick 而不是仅仅将其值设置为 showMenu。您只需使用 true 即可完成此操作。

这是一个例子:

setShowMenu(prev => !prev)

您的 const App = () => { const [showMenu, setShowMenu] = React.useState(false) const ref = React.useRef() useOnClickOutside(ref, () => setShowMenu(false)) return ( <div ref={ref}> <div className="menu-section-conatainer"> <button className={"menu-link"} onClick={() => { setShowMenu(prev => !prev) }} name="Toggle Menu" > {showMenu ? "Hide Menu" : "Show Menu"} </button> </div> <Menu showState={showMenu}> Menu Items </Menu> </div> ) } const Menu = ({ children, showState}) => { return ( <div className="menu-expanded-wrapper"> <div className={ showState ? "menu-expanded" : "menu-collapsed" }> {children} </div> </div> ) } 钩子没有变化。