React 下拉菜单只切换一次

Posted

技术标签:

【中文标题】React 下拉菜单只切换一次【英文标题】:React dropdown toggles only once 【发布时间】:2020-04-15 10:25:49 【问题描述】:

我正在尝试使用 useRef 钩子和 Typescript 构建 React Dropdown 组件: 如果我单击一次切换按钮或单击它外部,它会正确打开并关闭,但是当我想再次打开它时它会关闭。有任何想法吗 ?这是我以某种方式失去了参考参考吗?

这里是用法:

https://codesandbox.io/s/react-typescript-obdgs

import React,  useState, useRef, useEffect  from 'react'
import styled from 'styled-components'

interface Props 

const DropdownMenu: React.FC<Props> = ( children ) => 
  const [menuOpen, toggleMenu] = useState<boolean>(false)
  const menuContent = useRef<htmlDivElement>(null)

  useEffect(() => 
    // console.log(menuOpen)
  , [menuOpen])

  const showMenu = (event: React.MouseEvent) => 
    event.preventDefault()
    toggleMenu(true)
    document.addEventListener('click', closeMenu)
  

  const closeMenu = (event: MouseEvent) => 
    const el = event.target
    if (menuContent.current) 
      if (el instanceof Node && !menuContent.current.contains(el)) 
        toggleMenu(false)
        document.removeEventListener('click', closeMenu)
      
    
  

  return (
    <div>
      <button
        onClick=(event: React.MouseEvent) => 
          showMenu(event)
        
      >
        Open
      </button>
      menuOpen ? <div ref=menuContent>children</div> : null
    </div>
  )


export default DropdownMenu

【问题讨论】:

对我来说,代码沙箱工作正常。 有时能正常工作,有时不能,我不明白。 【参考方案1】:

如果您单击该按钮两次,您将无法再次打开它。如果您在按钮外部单击关闭,它将按预期工作。

这可能是因为您的 showMenu 回调即使在菜单已经显示时也会执行,这会导致附加多个 closeMenu 事件侦听器,从而导致奇怪的行为。

closeMenu 事件监听器应该在效果内创建,而不是在 showMenu 回调中。

const showMenu = (event: React.MouseEvent) => 
    event.preventDefault()
    toggleMenu(true)


// closeMenu is the same
const closeMenu = (event: MouseEvent) => 
    const el = event.target
    if (menuContent.current) 
        if (el instanceof Node && !menuContent.current.contains(el)) 
            toggleMenu(false)
            document.removeEventListener('click', closeMenu)
        
    


useEffect(() => 
    if (!menuOpen) 
        return
    
    document.addEventListener('click', closeMenu)
    return () => 
        document.removeEventListener('click', closeMenu)
    
, [menuOpen])

useEffect 真的很酷——移除事件监听器的返回函数将在 menuOpen 更改和组件卸载时调用。在您之前的代码中,如果要卸载组件,则不会删除事件侦听器。

【讨论】:

谢谢,非常好的和优雅的解决方案,很好的解释。【参考方案2】:

问题出在您的 onClick 按钮上。每次单击按钮时,您都在调用 showMenu,因此您每次都在添加新的事件侦听器。

如果菜单已显示,您不想致电showMenu,因此可以进行修复:

<button onClick=(event: React.MouseEvent) => 
  if (!menuOpen) showMenu(event);
>

【讨论】:

这仍可能导致附加多个事件侦听器。状态是异步的,这意味着如果用户点击非常快,状态将没有时间更新。

以上是关于React 下拉菜单只切换一次的主要内容,如果未能解决你的问题,请参考以下文章

下拉菜单不切换且类似于按钮

如果它具有表单元素,如何防止引导下拉菜单切换回来

引导导航下拉菜单切换问题

vue+css动画实现下拉菜单上、下箭头切换

单击时切换下拉菜单并隐藏所有其他下拉菜单

从打开的 Bootstrap 3 下拉菜单切换到不同的下拉菜单需要额外点击移动设备