如何在Next.js中实现点击任意位置关闭Dropdown的功能
修复点击外部关闭下拉菜单的逻辑
你的useClickOutside Hook的点击判断逻辑存在错误,导致当前行为不符合预期。正确的逻辑应该是:当点击事件的目标不在下拉菜单元素内部时,触发关闭回调。
修正后的useClickOutside Hook
import { useEffect, useRef } from "react"; export function useClickOutside(callback: () => void) { const ref = useRef<HTMLDivElement | null>(null); useEffect(() => { function handleClick(event: MouseEvent) { // 确认ref已挂载,且点击目标不在下拉菜单元素范围内 if (ref.current && !ref.current.contains(event.target as Node)) { callback(); } } window.addEventListener("click", handleClick, { capture: true }); return () => { window.removeEventListener("click", handleClick, { capture: true }); }; }, [callback]); return ref; }
原代码的问题点
- 判断逻辑完全颠倒:原代码用
target?.contains(ref.current)判断,实际应该检查点击目标是否被ref.current包含 - 错误的依赖项:
useEffect中加入ref.current毫无意义,useRef的current值变更不会触发副作用重执行 - 冗余判断:
event.target === ref.current的判断多余,contains方法已经包含元素自身的情况 - 无用导入:
toUSVString未被使用,可直接移除
调整DropDown组件
点击菜单按钮时需要阻止事件冒泡,避免刚打开菜单就被外部点击逻辑误关闭:
import { ReactNode, MouseEvent } from "react"; import { useClickOutside } from "../../util/hooks/useClickOutside"; export default function DropDown({ id, onOpen, onClose, isOpen, content, buttonContent, }: { id: string; onOpen: (id: string) => void | undefined; onClose: () => void | undefined; isOpen: boolean; content?: ReactNode; buttonContent?: any; }) { const elementRef = useClickOutside(() => { if (isOpen) { onClose(); } }); const handleButtonClick = (e: MouseEvent<HTMLButtonElement>) => { e.stopPropagation(); isOpen ? onClose() : onOpen(id); }; return ( <div className="relative z-30"> <button className="rounded-full p-0 border-2 border-slate-300" onClick={handleButtonClick} > {buttonContent} </button> {isOpen && ( <div ref={elementRef} className="absolute space-y-2 right-0 w-56 mt-2 origin-top-right bg-white rounded-lg shadow-xl ring-1 ring-black ring-opacity-5 focus:outline-none z-30" > {content} </div> )} </div> ); }
内容的提问来源于stack exchange,提问作者Benjamin Kalungi
相关产品推荐
相关产品推荐

