首页 > 解决方案 > React - 我的自定义钩子在元素外点击有什么问题?

问题描述

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

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])
}

标签: reactjs

解决方案


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

您可以通过将使用移动useOnClickOutsideApp组件并将引用放置在按钮和菜单的父元素上来解决此问题,以便出于“外部”的目的而忽略对按钮的单击。此外,无论如何,这可能是一个更好的结构,因此打开/关闭逻辑仅包含在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>
  )
}

没有改变你的useOnClickOutside钩子。


推荐阅读