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

如何在React.js中实现搜索防抖?遇TypeError问题求助

问题:React搜索输入防抖实现报错:TypeError: Cannot read properties of null (reading 'value')

我尝试在React搜索输入上实现防抖功能,期望用户停止输入后再处理搜索逻辑,但添加防抖后抛出错误:TypeError: Cannot read properties of null (reading 'value')。以下是相关组件代码:

import React, { useState } from "react";
import data from "./MOCK_DATA.json";
function App() {
  const [name, setName] = useState("");
  const debounce = (func, delay) => {
    let timer;
    return (...arg) => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        func(...arg);
      }, delay);
    };
  };

  const onChangeHandler = (e) => {
    setName(e.target.value);
  };

  const debouncedOnChangeHandler = debounce(onChangeHandler, 1000);

  const filteredData = data.filter((el) =>
    el.first_name.toLowerCase().includes(name.toLowerCase())
  );
  return (
    <div>
      <div style={{ width: "100%" }}>Search</div>
      <input
        value={name}
        onChange={debouncedOnChangeHandler} //debouncedOnChangeHandler doesn't work because value is asynchronous
        //onChange={onChangeHandler} //this works fine without debounce function
        type="text"
        placeholder="search"
      />
      {filteredData &&
        filteredData.map((el) => <div key={el.id}>{el.first_name}</div>)}
    </div>
  );
}

export default App;

问题原因与修复方案

原因

React的合成事件对象(SyntheticEvent)会在事件处理同步逻辑完成后被自动复用或销毁。防抖函数通过setTimeout延迟执行,当延迟的回调触发时,原事件对象已经变成null,因此无法读取e.target.value。

修复方案

方案一:提前提取输入值(推荐)

在同步的输入事件处理中先提取输入值,再将值传递给防抖函数,避免依赖已被回收的事件对象:

import React, { useState } from "react";
import data from "./MOCK_DATA.json";

function App() {
  const [name, setName] = useState("");

  const debounce = (func, delay) => {
    let timer;
    return (...arg) => {
      if (timer) clearTimeout(timer);
      timer = setTimeout(() => {
        func(...arg);
      }, delay);
    };
  };

  // 直接接收输入值,不再依赖事件对象
  const onChangeHandler = (inputValue) => {
    setName(inputValue);
  };

  const debouncedOnChangeHandler = debounce(onChangeHandler, 1000);

  // 同步提取输入值,传给防抖函数
  const handleInput = (e) => {
    const value = e.target.value;
    debouncedOnChangeHandler(value);
  };

  const filteredData = data.filter((el) =>
    el.first_name.toLowerCase().includes(name.toLowerCase())
  );

  return (
    <div>
      <div style={{ width: "100%" }}>Search</div>
      <input
        value={name}
        onChange={handleInput}
        type="text"
        placeholder="search"
      />
      {filteredData.map((el) => (
        <div key={el.id}>{el.first_name}</div>
      ))}
    </div>
  );
}

export default App;

方案二:使用e.persist()保留事件对象

调用事件对象的persist()方法,让React不回收该事件对象,确保延迟回调执行时仍能访问事件属性:

const onChangeHandler = (e) => {
  e.persist(); // 保留事件对象,阻止React回收
  setName(e.target.value);
};

const debouncedOnChangeHandler = debounce(onChangeHandler, 1000);

方案对比

  • 方案一更推荐:逻辑清晰,直接传递值,避免依赖事件对象的生命周期,性能更可控。
  • 方案二更简洁:但会让事件对象保留在内存中,可能带来微小的性能开销。

内容的提问来源于stack exchange,提问作者FD3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 12:15:41