React点击弹窗触发按钮后外部点击事件立即执行的问题求助
解决React弹窗按钮点击后外部点击事件立即触发的问题
问题原因
点击弹窗触发按钮后,外部点击事件立即执行的核心原因是事件冒泡机制:
- 用户点击按钮时,按钮的
onClick事件触发,更新showOptions状态; - 组件重新渲染,
useOutSideClick钩子因condition(showOptions变为true)执行,给document添加点击监听器; - 按钮的点击事件尚未完成冒泡,会继续传递到
document,此时刚添加的监听器会捕获到这个事件; - 由于弹窗是在
showOptions为true时才渲染,此时boxRef.current还未被赋值,!ref.current.contains(event.target)判定为true,触发弹窗关闭逻辑。
解决方案
方案1:阻止按钮点击事件的冒泡
最直接的解决方式是在按钮的点击事件中阻止事件冒泡,避免事件传递到document。
修改useAxis钩子,传递事件对象:
import React, { useEffect, useRef, useState } from "react"; const useAxis = (children, events) => { const childRef = useRef(); const [offset, setOffset] = useState({ x: "", y: "", }); useEffect(() => { Object.keys(events).forEach((event) => { let eventHandler; let callBack = events[event]; if (event === "onClick" || event === "onMouseEnter") { // 将事件对象传递给回调函数 eventHandler = (e) => { callBack(e); }; events[event] = eventHandler; } }); }, [JSON.stringify(events)]); const childrens = React.Children.map(children, (child) => { return React.cloneElement(child, { ...events, ref: childRef, }); }); useEffect(() => { setOffset(() => { return { x: childRef.current.offsetLeft, y: childRef.current.offsetTop, }; }); }, []); return { childrens, offset }; }; export default useAxis;
修改Options组件的点击回调,阻止冒泡:
const { childrens, offset } = useAxis(children, { onClick: (e) => { console.log("test1"); // 阻止事件冒泡到document e.stopPropagation(); setShowOptions(!showOptions); }, });
方案2:延迟添加document点击监听器
通过setTimeout延迟添加监听器,让React有足够时间渲染弹窗,确保boxRef.current已被正确赋值,此时contains判断能准确识别点击是否在弹窗内部。
修改useOutSideClick钩子:
import { useEffect } from "react"; const useOutSideClick = (ref, outSideClickHandler, condition = true) => { useEffect(() => { if (condition) { const handleClickOutside = (event) => { console.log("hellloooo"); if (ref.current && !ref.current.contains(event.target)) { outSideClickHandler(); } }; // 延迟0ms添加,确保弹窗已渲染完成 const timer = setTimeout(() => { document.addEventListener("click", handleClickOutside); }, 0); return () => { clearTimeout(timer); document.removeEventListener("click", handleClickOutside); }; } }, [ref, condition]); }; export default useOutSideClick;
总结
方案1通过阻止冒泡从根源上避免了事件传递到document,是更可靠的解决方案;方案2利用延迟执行确保弹窗渲染完成,适合无法修改按钮事件的场景。两种方案都能解决点击按钮后外部事件立即触发的问题。
内容的提问来源于stack exchange,提问作者Saman
相关产品推荐
相关产品推荐

