反应下拉列表仅切换一次

时间:2020-04-15 10:25:49

标签: javascript reactjs typescript react-hooks use-ref

我正在尝试使用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

2 个答案:

答案 0 :(得分: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以及卸载组件时,都将调用删除事件侦听器的返回函数。在您之前的代码中,如果要卸载该组件,则不会删除事件侦听器。

答案 1 :(得分:0)

问题出在您的onClick按钮上。您每次点击按钮都在呼叫showMenu,因此每次都添加新的事件监听器。

如果菜单已经显示,您不想呼叫showMenu,因此可以解决此问题:

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