React组件onClick与mousedown事件冲突问题解决咨询
解决React下拉菜单标签onClick失效问题(保留点击外部关闭功能)
问题描述
我有一个带下拉菜单的React个人资料按钮组件,菜单包含"My Profile"、"Settings"、"Logout"三个选项。当前遇到的问题是:菜单内标签的onClick事件无法触发(无法打印console.log),但移除实现点击外部关闭菜单的useEffect中的addEventListener后,onClick就能正常工作。需要在保留点击外部关闭菜单功能的前提下解决这个问题。
原代码
'use client' // Importe as bibliotecas necessárias import React, { useState, useRef, useEffect } from 'react' //ICON import { FaCircleUser } from 'react-icons/fa6' import { AiOutlineUser, AiOutlineSetting, AiOutlineLogout } from 'react-icons/ai' const ProfileButton = () => { // Estado para controlar a exibição do menu const [isMenuOpen, setIsMenuOpen] = useState(false) // Referência para o botão de perfil const profileButtonRef = useRef() // Função para fechar o menu quando clicar fora do botão const handleClickOutside = event => { if ( profileButtonRef.current && !profileButtonRef.current.contains(event.target) ) { setIsMenuOpen(false) } } // Adiciona um event listener para fechar o menu ao clicar fora useEffect(() => { document.addEventListener('mousedown', handleClickOutside) // Remove o event listener ao desmontar o componente return () => { document.removeEventListener('mousedown', handleClickOutside) } }, []) return ( <div className="relative inline-block text-left"> {/* Botão de perfil */} <button ref={profileButtonRef} className="text-3xl text-zinc-700 py-2 px-4 rounded-full focus:outline-none cursor-pointer" onClick={() => setIsMenuOpen(!isMenuOpen)} > <FaCircleUser /> </button> {/* Menu de opções */} {isMenuOpen && ( <div className="origin-top-right absolute right-0 mt-2 w-50 bg-white border border-gray-300 rounded-md shadow-lg z-50"> <div className="py-1"> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center z-999" onClick={() => console.log('My Profile')} > <AiOutlineUser className="mr-2" size={18} /> My Profile </a> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center" onClick={() => console.log('Settings')} > <AiOutlineSetting className="mr-2" size={18} /> Configurações </a> </div> <div className="border-t border-gray-300"> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center" onClick={() => console.log('logout')} > <AiOutlineLogout className="mr-2" size={18} /> Sair </a> </div> </div> )} </div> ) } export default ProfileButton
问题原因
profileButtonRef仅绑定在个人资料按钮上,下拉菜单不在该ref的DOM范围内。当点击菜单内的标签时,handleClickOutside会先触发,将isMenuOpen设为false,导致菜单组件被立即卸载,标签的onClick事件因此无法执行。
解决方案
方法一:将ref绑定到包含按钮和菜单的父容器
把ref绑定到包裹按钮和菜单的最外层div上,确保菜单属于ref的DOM范围,这样点击菜单内部时不会触发关闭逻辑。
修改后的完整代码:
'use client' // Importe as bibliotecas necessárias import React, { useState, useRef, useEffect } from 'react' //ICON import { FaCircleUser } from 'react-icons/fa6' import { AiOutlineUser, AiOutlineSetting, AiOutlineLogout } from 'react-icons/ai' const ProfileButton = () => { // Estado para controlar a exibição do menu const [isMenuOpen, setIsMenuOpen] = useState(false) // 把ref绑定到包含按钮和菜单的父容器 const profileContainerRef = useRef() // Função para fechar o menu quando clicar fora do容器 const handleClickOutside = event => { if ( profileContainerRef.current && !profileContainerRef.current.contains(event.target) ) { setIsMenuOpen(false) } } // Adiciona um event listener para fechar o menu ao clicar fora useEffect(() => { document.addEventListener('mousedown', handleClickOutside) // Remove o event listener ao desmontar o componente return () => { document.removeEventListener('mousedown', handleClickOutside) } }, []) return ( // 将ref绑定到这个父容器上 <div ref={profileContainerRef} className="relative inline-block text-left"> {/* Botão de perfil */} <button className="text-3xl text-zinc-700 py-2 px-4 rounded-full focus:outline-none cursor-pointer" onClick={() => setIsMenuOpen(!isMenuOpen)} > <FaCircleUser /> </button> {/* Menu de opções */} {isMenuOpen && ( <div className="origin-top-right absolute right-0 mt-2 w-50 bg-white border border-gray-300 rounded-md shadow-lg z-50"> <div className="py-1"> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center z-999" onClick={() => console.log('My Profile')} > <AiOutlineUser className="mr-2" size={18} /> My Profile </a> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center" onClick={() => console.log('Settings')} > <AiOutlineSetting className="mr-2" size={18} /> Configurações </a> </div> <div className="border-t border-gray-300"> <a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center" onClick={() => console.log('logout')} > <AiOutlineLogout className="mr-2" size={18} /> Sair </a> </div> </div> )} </div> ) } export default ProfileButton
方法二:在标签的onClick中阻止事件传播
如果不想修改ref的绑定,可以在每个的onClick事件中添加event.stopPropagation(),阻止事件冒泡到document层面,这样handleClickOutside不会被触发:
示例:
<a href="#" className="px-4 py-2 text-gray-800 hover:bg-gray-200 flex items-center" onClick={(e) => { e.stopPropagation() console.log('My Profile') }} > <AiOutlineUser className="mr-2" size={18} /> My Profile </a>
注意:方法二可能会影响菜单内其他需要事件冒泡的功能,优先推荐方法一。
内容的提问来源于stack exchange,提问作者Guilherme Migliano
相关产品推荐
相关产品推荐

