React自定义带搜索框下拉组件展开时自动聚焦问题求助
下拉框展开时自动聚焦搜索框的问题解决
问题背景
开发带搜索框的下拉组件时,期望下拉框展开时自动聚焦搜索框,但出现以下问题:
- 首次触发展开事件时,
useRef获取的输入框DOM节点为空,聚焦操作失效 - 再次切换下拉框时,
ref已存在,但聚焦仍无效果 - 尝试用
forwardRef包裹输入组件、将ref加入useEffect依赖均未解决,推测核心原因是下拉框展开时输入框尚未完成DOM渲染
解决方案
核心思路是跟踪下拉框的展开状态,在状态变为展开且输入框DOM渲染完成后触发聚焦,具体实现步骤如下:
1. 新增状态跟踪下拉框展开状态
在组件内部添加状态变量,记录下拉框的展开/收起状态:
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
2. 更新onToggle回调,同步展开状态
修改原有的parentToggled函数,同步更新下拉框状态:
const parentToggled = (isOpen) => { setIsDropdownOpen(isOpen); // 同步下拉框展开状态 if (isOpen) { const filtered = allParents.filter(option => option.label.toLowerCase().includes(parentSearchTerm.toLowerCase()) ); setParentsFiltered(filtered.slice(0, SCROLL_SIZE)); } };
3. 用useEffect监听展开状态,完成后执行聚焦
利用useEffect监听展开状态变化,结合requestAnimationFrame确保DOM渲染完成后再执行聚焦:
useEffect(() => { if (isDropdownOpen && parentRef.current) { // 等待DOM渲染完成后执行聚焦 requestAnimationFrame(() => { parentRef.current.focus(); }); } }, [isDropdownOpen]);
4. 简化输入组件(可选)
react-bootstrap的FormControl本身支持接收ref,无需额外用forwardRef包裹,可直接使用原组件:
<FormControl ref={parentRef} id="parentInput" type="text" placeholder="Search..." value={parentSearchTerm} onChange={handleParentSearch} />
修改后核心代码片段
import React, { useState, useEffect, useRef } from 'react'; import { Dropdown, ButtonGroup, FormControl } from 'react-bootstrap'; // 省略其他常量、状态及辅助函数定义 const CompaniesNav = () => { const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [parentsFiltered, setParentsFiltered] = useState([]); const [parentSearchTerm, setParentSearchTerm] = useState(''); const parentRef = useRef(null); const parentToggled = (isOpen) => { setIsDropdownOpen(isOpen); if (isOpen) { const filtered = allParents.filter(option => option.label.toLowerCase().includes(parentSearchTerm.toLowerCase()) ); setParentsFiltered(filtered.slice(0, SCROLL_SIZE)); } }; const handleParentSearch = (e) => { setParentSearchTerm(e.target.value); // 保留原有搜索过滤逻辑 }; useEffect(() => { if (isDropdownOpen && parentRef.current) { requestAnimationFrame(() => { parentRef.current.focus(); }); } }, [isDropdownOpen]); return ( <> <Dropdown as={ButtonGroup} onToggle={parentToggled}> <Dropdown.Toggle variant="company" id="dropdown-basic"> <div className="dropdown-container"> <div className="dropdown-title">Company Parent</div> <div className="dropdown-value">{selectedParent ? selectedParent : "Select..."}</div> <div className="dropdown-clear" onClick={clearParentDropdown}><i className="fa fa-close" /></div> <span className="dropdown-line"></span> <div className="dropdown-caret"><i className="fa fa-chevron-down" /></div> </div> </Dropdown.Toggle> <Dropdown.Menu variant='company'> <FormControl ref={parentRef} id="parentInput" type="text" placeholder="Search..." value={parentSearchTerm} onChange={handleParentSearch} /> <Dropdown.Divider /> {parentsFiltered.length !== 0 ? parentsFiltered.map((option) => ( <Dropdown.Item eventKey={option.value} key={option.value} onClick={() => filterParent(option.value, option.label)} href="#">{option.label}</Dropdown.Item> )) : <> <Dropdown.Item eventKey="1" href="#">Empty</Dropdown.Item> </>} </Dropdown.Menu> </Dropdown> </> ); };
内容的提问来源于stack exchange,提问作者kpg
相关产品推荐
相关产品推荐

