You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为条件渲染的用户下拉菜单添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 16:16:10