如何仅让DOM中激活的组件调用自定义useOutsideClick Hook?
问题:仅让激活状态的HeaderSearch触发useOutsideClick Hook
问题原因
页面中同时存在移动端和桌面端两个HeaderSearch组件实例(通过CSS的display:none控制隐藏/显示),两个实例都注册了useOutsideClick的全局点击事件监听,因此点击组件外部时,两个实例的回调都会触发。
解决方案
方案1:给useOutsideClick添加启用状态控制
修改自定义Hook,增加enabled参数,仅在组件激活时注册事件监听:
const useOutsideClick = (callback, enabled = true) => { const reference = useRef(); useEffect(() => { if (!enabled) return; const handleClick = (event) => { if (reference.current && !reference.current.contains(event.target)) { callback(); } }; document.addEventListener("click", handleClick); return () => document.removeEventListener("click", handleClick); }, [reference, callback, enabled]); return reference; };
在父组件中,给当前激活版本的HeaderSearch传递isActive为true,非激活版本传false:
// 父组件渲染逻辑 <HeaderSearch screenWidth="mobile" isActive={currentScreen === "mobile"} /> <HeaderSearch screenWidth="desk" isActive={currentScreen === "desk"} />
然后在HeaderSearch组件中使用Hook:
const HeaderSearch = ({ screenWidth, isActive }) => { // ...其他逻辑 const searchRef = useOutsideClick(test, isActive); return ( <div id="search-header" className={`search-header ${screenWidth}`} ref={searchRef}> {/* 内部元素 */} </div> ); };
方案2:在回调中判断组件可见性
无需修改Hook,直接在回调中检查组件是否可见,仅在可见时执行逻辑:
function test(ref) { const element = ref.current; if (!element) return; // 判断元素是否处于显示状态 const isVisible = window.getComputedStyle(element).display !== 'none'; if (isVisible) { console.log("clicked outside"); // 执行你的关闭/处理逻辑 } }
修改Hook,将ref传递给回调:
const useOutsideClick = (callback) => { const reference = useRef(); useEffect(() => { const handleClick = (event) => { if (reference.current && !reference.current.contains(event.target)) { callback(reference); } }; document.addEventListener("click", handleClick); return () => document.removeEventListener("click", handleClick); }, [reference, callback]); return reference; };
组件中使用:
const HeaderSearch = ({ screenWidth }) => { // ...其他逻辑 const searchRef = useOutsideClick((ref) => test(ref)); // 返回对应版本的div,ref绑定为searchRef };
方案3:优化渲染逻辑,仅渲染激活版本
在父组件中只渲染匹配当前屏幕尺寸的HeaderSearch,避免DOM中存在两个实例:
// 父组件渲染逻辑 {currentScreen === "mobile" ? ( <HeaderSearch screenWidth="mobile" /> ) : ( <HeaderSearch screenWidth="desk" /> )}
这样DOM中始终只有一个HeaderSearch实例,useOutsideClick只会注册一次,自然不会出现重复触发的问题。
内容的提问来源于stack exchange,提问作者GoldenBags
相关产品推荐
相关产品推荐

