如何在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
相关产品推荐
相关产品推荐

