You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:09:18