React中SearchInput/TextInput每输入一个字符就自动失焦问题
问题根因
输入一次就失焦的核心原因是:你在父组件的渲染函数内部直接定义了Header子组件。
每次输入触发setSearch/setFilteredResults更新时,父组件会重新执行渲染逻辑,此时内部声明的Header会被重新创建,是和上一次渲染完全不同的组件引用。React做虚拟DOM对比时,会判定旧的Header已经被卸载,重新挂载全新的Header实例,内部的SearchInput输入框DOM也会跟着被销毁重建,原本聚焦的旧DOM被移除,自然就出现了输入一个字符就失焦的现象。
另外你的代码还存在一个隐藏的闭包bug:searchItems里调用setSearch(searchValue)后,立刻读取的search是上一轮渲染的旧状态值,不是刚更新的新值,会导致筛选逻辑和输入内容不同步。
修复方法
- 不要在组件内部声明子组件,将
Header组件移到父组件外部定义,需要用到的父组件状态、回调通过props传入,保证组件引用在重渲染时保持稳定。 - 修正筛选逻辑的闭包问题,过滤时直接使用传入的最新输入值,不要依赖刚触发更新的state,避免筛选结果不同步。
修复后完整代码
// 把Header移到父组件外部定义,保证组件引用稳定 const Header = ({ searchValue, onSearchChange }) => ( <Box> <SearchInput placeholder="Search" value={searchValue} onChange={(e) => onSearchChange(e.target.value)} /> </Box> ); function index() { const list = [//data] const [search, setSearch] = useState(""); const [filteredResults, setFilteredResults] = useState(list); const searchItems = (searchValue) => { setSearch(searchValue); // 直接使用传入的最新输入值做判断,避免闭包拿到旧state if (searchValue !== "") { const filteredData = partners.filter((item) => { return Object.values(item) .join("") .toLowerCase() .includes(searchValue.toLowerCase()); }); setFilteredResults(filteredData); } else { setFilteredResults(partners); } }; return ( <Parent headerContent={<Header searchValue={search} onSearchChange={searchItems} />} > <Box> <Table data={search.length > 1 ? filteredResults : list} /> </Box> </Parent> ); } export default index;
补充:如果不想单独抽离Header组件,也可以直接将SearchInput相关的JSX内容直接传入
headerContent属性,无需在父组件内部声明成独立组件,同样可以避免组件重挂载导致的失焦问题。
内容的提问来源于stack exchange,提问作者Salvo
相关产品推荐
相关产品推荐

