React使用事件监听器切换组件时第二次点击图标无法显示搜索框
问题根因
你的代码存在两个核心问题导致二次点击失效:
- 事件监听绑定/移除逻辑错误:每次
showSearch变化时都会新增一个document点击监听器,且绑定和移除时用的是不同的匿名函数,旧监听器根本无法被移除,多个监听器叠加后闭包捕获的旧showSearch状态发生冲突。 - 闭包捕获问题:事件处理函数捕获的是每次绑定时刻的
showSearch值,不是最新的状态,导致判断逻辑不符合预期。
修复后代码
import ReactDOM from "react-dom"; import "@elastic/eui/dist/eui_theme_amsterdam_light.css"; import React, { useEffect, useRef, useState } from "react"; import { EuiButtonIcon, EuiFieldText } from "@elastic/eui"; const App = () => { const [showSearch, setShowSearch] = useState(false); const searchInputRef = useRef(null); // 存储最新的showSearch状态,避免闭包读取旧值 const showSearchRef = useRef(showSearch); // 同步状态到ref useEffect(() => { showSearchRef.current = showSearch; }, [showSearch]); useEffect(() => { const handleClickOutside = (event) => { if ( searchInputRef.current && !searchInputRef.current.contains(event.target) && showSearchRef.current ) { setShowSearch(false); } }; // 只绑定一次事件 document.addEventListener("click", handleClickOutside, false); // 组件卸载时移除同一个事件处理函数 return () => { document.removeEventListener("click", handleClickOutside, false); }; }, []); if (showSearch) { return ( <EuiFieldText icon="search" placeholder="Sample Search Placeholder ..." inputRef={searchInputRef} /> ); } return ( <EuiButtonIcon aria-label="button" iconType="search" iconSize="xxl" onClick={() => setShowSearch(true)} /> ); }; export default App;
修改说明
- 新增
showSearchRef实时同步最新的显示状态,解决闭包读取旧值的问题 - 全局只做一次事件绑定,用同一个函数完成绑定/移除操作,彻底避免监听器残留
- 去掉了冗余的多监听器逻辑,运行时不会再出现状态判断冲突的问题
内容的提问来源于stack exchange,提问作者Jimmy
相关产品推荐
相关产品推荐

