ReactJS问题:输入框失焦导致下拉菜单按钮事件无法触发
问题:搜索框下拉菜单按钮点击失效
我需要在React中实现一个搜索框,输入内容时弹出下拉菜单,菜单内的按钮可触发事件。要求是当应用中其他输入框被使用时,该下拉菜单需消失。目前已实现该功能,但存在问题:点击下拉菜单按钮时,输入框先失焦导致下拉菜单消失,按钮事件无法触发。
Searchbox组件代码
import React, { useState } from 'react'; import Dropdown from './Dropdown'; function Search(props) { const [focused, setFocused] = useState(false); const inputHandler = (params) => { if (params.length > 0) props.apiCall(params); } const buttonHandler = (id) => { console.log(id); } return ( <> <input type="text" placeholder="Suchen.." onChange={(event) => inputHandler(event.target.value)} onFocus={() => setFocused(true)} onBlur={() => setFocused(false)} // 问题所在 /> { focused === true && props.apiData.length > 0 ? props.apiData.map((mappedData, key) => { return ( <Dropdown key={key} id={mappedData.id} name={mappedData.name} /* even more Data */ buttonHandler={buttonHandler} /> ) }) : null } </> ) } export default Search;
Dropdown组件代码
import React from 'react'; function Dropdown(props) { return ( <ul key={props.id}> <li>{props.name}</li> <li>even more data</li> <li> <input type="button" value="Select" onClick={() => { props.buttonHandler(props.id) }} /> </li> </ul> ) } export default Dropdown;
解决方案
这个问题的核心是输入框的onBlur事件会先于按钮的onClick事件触发,导致下拉菜单提前卸载,按钮点击事件无法执行。这里提供两种可行的解决思路:
方案1:给onBlur添加延迟,让按钮事件先执行
通过setTimeout延迟隐藏下拉菜单,同时在按钮点击时取消这个延迟,确保事件能正常触发。
修改Search组件的代码:
import React, { useState, useRef } from 'react'; import Dropdown from './Dropdown'; function Search(props) { const [focused, setFocused] = useState(false); const blurTimeoutRef = useRef(null); // 保存延迟ID const inputHandler = (params) => { if (params.length > 0) props.apiCall(params); } const buttonHandler = (id) => { // 取消延迟,防止下拉菜单消失 clearTimeout(blurTimeoutRef.current); console.log(id); // 执行完逻辑后手动隐藏下拉菜单 setFocused(false); } return ( <> <input type="text" placeholder="Suchen.." onChange={(event) => inputHandler(event.target.value)} onFocus={() => { // 聚焦时清除之前的延迟 clearTimeout(blurTimeoutRef.current); setFocused(true); }} onBlur={() => { // 延迟200毫秒隐藏下拉菜单,给按钮点击留执行时间 blurTimeoutRef.current = setTimeout(() => { setFocused(false); }, 200); }} /> { focused === true && props.apiData.length > 0 ? props.apiData.map((mappedData, key) => { return ( <Dropdown key={key} id={mappedData.id} name={mappedData.name} buttonHandler={buttonHandler} /> ) }) : null } </> ) } export default Search;
方案2:监听全局点击,判断点击区域
通过监听document的点击事件,判断点击目标是否属于搜索框或下拉菜单,只有点击外部时才隐藏下拉菜单,避免内部点击触发隐藏。
修改Search组件的代码:
import React, { useState, useRef, useEffect } from 'react'; import Dropdown from './Dropdown'; function Search(props) { const [focused, setFocused] = useState(false); const searchContainerRef = useRef(null); // 包裹搜索框和下拉菜单的容器 const inputHandler = (params) => { if (params.length > 0) props.apiCall(params); } const buttonHandler = (id) => { console.log(id); setFocused(false); } useEffect(() => { const handleClickOutside = (e) => { // 如果点击目标不在容器内,隐藏下拉菜单 if (searchContainerRef.current && !searchContainerRef.current.contains(e.target)) { setFocused(false); } }; // 添加全局点击监听 document.addEventListener('mousedown', handleClickOutside); return () => { // 组件卸载时移除监听 document.removeEventListener('mousedown', handleClickOutside); }; }, []); return ( <div ref={searchContainerRef}> {/* 添加容器ref */} <input type="text" placeholder="Suchen.." onChange={(event) => inputHandler(event.target.value)} onFocus={() => setFocused(true)} // 移除原来的onBlur /> { focused === true && props.apiData.length > 0 ? props.apiData.map((mappedData, key) => { return ( <Dropdown key={key} id={mappedData.id} name={mappedData.name} buttonHandler={buttonHandler} /> ) }) : null } </div> ) } export default Search;
两种方案都能解决问题,方案2的体验更流畅,不会有延迟感,推荐优先使用。
内容的提问来源于stack exchange,提问作者Parry Hotter
相关产品推荐
相关产品推荐

