我正在尝试使用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
答案 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);
}}>