如何为条件渲染的用户下拉菜单添加onClick点击触发功能
实现登录后点击账户图标弹出下拉菜单的方案
要解决点击图标才弹出菜单的问题,核心是用状态控制菜单的显示/隐藏,再结合点击事件切换状态,同时补充外部点击关闭、ESC键关闭的优化体验,具体步骤如下:
1. 引入状态管理菜单显示
用React的useState钩子创建一个布尔值状态isMenuOpen,用来标记菜单是否展开:
import { useState, useEffect, useRef } from 'react';
2. 给账户图标添加点击事件
给UserCircleIcon绑定onClick事件,点击时切换isMenuOpen的状态,同时加上cursor-pointer让图标看起来可点击:
<UserCircleIcon className="h-6 w-6 cursor-pointer flex-shrink-0 text-gray-400 group-hover:text-gray-500" aria-hidden="true" onClick={() => setIsMenuOpen(prev => !prev)} // 点击切换菜单显示状态 />
3. 修改菜单的渲染条件
原来只判断user存在就显示,现在要同时满足user存在且isMenuOpen为true:
{user && isMenuOpen && ( // 菜单内容 )}
4. 优化:点击外部区域关闭菜单
添加useRef获取菜单和图标元素,再用useEffect监听全局点击事件,判断点击位置是否在菜单或图标之外,若是则关闭菜单:
const menuRef = useRef(null); const iconRef = useRef(null); useEffect(() => { const handleClickOutside = (e) => { if (menuRef.current && !menuRef.current.contains(e.target) && !iconRef.current.contains(e.target)) { setIsMenuOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, []);
记得给图标和菜单加上ref,同时调整菜单的mt-36为更合理的mt-8(避免菜单离图标过远):
<UserCircleIcon ref={iconRef} // 其他属性 /> <div ref={menuRef} className="w-24 z-10 rounded-md bg-white py-1 shadow-lg flex flex-col absolute mt-8 right-0 ring-1 ring-black ring-opacity-5 focus:outline-none" > {/* 菜单选项 */} </div>
5. 优化:按ESC键关闭菜单
在同一个useEffect里添加键盘事件监听,按下ESC键时关闭菜单:
useEffect(() => { const handleClickOutside = (e) => { if (menuRef.current && !menuRef.current.contains(e.target) && !iconRef.current.contains(e.target)) { setIsMenuOpen(false); } }; const handleEscKey = (e) => { if (e.key === 'Escape') { setIsMenuOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleEscKey); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleEscKey); }; }, []);
完整代码示例
import { useState, useEffect, useRef } from 'react'; import { useSelector } from 'react-redux'; import { UserCircleIcon } from '@heroicons/react/24/outline'; export default function Example() { const user = useSelector((state) => state.user); const [isMenuOpen, setIsMenuOpen] = useState(false); const menuRef = useRef(null); const iconRef = useRef(null); useEffect(() => { const handleClickOutside = (e) => { if (menuRef.current && !menuRef.current.contains(e.target) && !iconRef.current.contains(e.target)) { setIsMenuOpen(false); } }; const handleEscKey = (e) => { if (e.key === 'Escape') { setIsMenuOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleEscKey); return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleEscKey); }; }, []); return ( <div className="bg-white"> <div className="ml-auto flex items-center"> <div className="hidden relative lg:flex lg:flex-1 lg:items-center lg:justify-end lg:space-x-6"> <UserCircleIcon ref={iconRef} className="h-6 w-6 cursor-pointer flex-shrink-0 text-gray-400 group-hover:text-gray-500" aria-hidden="true" onClick={() => setIsMenuOpen(prev => !prev)} /> {user && isMenuOpen && ( <div ref={menuRef} className="w-24 z-10 rounded-md bg-white py-1 shadow-lg flex flex-col absolute mt-8 right-0 ring-1 ring-black ring-opacity-5 focus:outline-none" > <p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 transition-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700"> Profile </p> <p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 transition-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700"> Settings </p> <p className="bg-gray-50 flex items-center gap-3 cursor-pointer hover:bg-slate-100 transition-all duration-100 ease-in-out px-4 py-2 text-sm text-gray-700"> Signout </p> </div> )} </div> </div> </div> ); }
(注:修正了原代码中trainstion的拼写错误,改为正确的transition)
内容的提问来源于stack exchange,提问作者jimzhou
相关产品推荐
相关产品推荐

