题目
实现一个带防抖功能的搜索输入组件
信息
- 类型:问答
- 难度:⭐⭐
考点
受控组件, 防抖优化, 性能优化, 事件处理
快速回答
实现要点:
- 使用
useState管理输入值 - 通过
useEffect和setTimeout实现防抖 - 在防抖后触发搜索API调用
- 组件卸载时清除定时器
- 正确处理异步操作竞态条件
问题场景
在搜索功能中,用户连续输入时会频繁触发API请求,导致性能问题和无效请求。需要实现:
- 受控输入组件管理状态
- 防抖机制延迟请求
- 避免内存泄漏和竞态条件
核心实现
import { useState, useEffect } from 'react';
const SearchInput = ({ onSearch }) => {
const [query, setQuery] = useState('');
useEffect(() => {
// 设置防抖定时器
const handler = setTimeout(() => {
if (query.trim()) {
onSearch(query);
}
}, 500);
// 清除定时器
return () => clearTimeout(handler);
}, [query, onSearch]);
return (
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
/>
);
};
原理说明
- 受控组件:输入值由React状态完全控制
- 防抖原理:延迟执行函数,连续触发时重置计时器
- useEffect清理:组件卸载或依赖变更时清除前次定时器
最佳实践
- 防抖时间:根据场景选择300-500ms(搜索建议通常500ms)
- 空值处理:忽略空查询或显示默认结果
- 竞态处理:使用AbortController取消过期请求
useEffect(() => { const controller = new AbortController(); const fetchData = async () => { try { const results = await fetch(`/api/search?q=${query}`, { signal: controller.signal }); // 处理结果 } catch (err) { if (err.name !== 'AbortError') { // 处理真实错误 } } }; const timer = setTimeout(fetchData, 500); return () => { controller.abort(); clearTimeout(timer); }; }, [query]);
常见错误
| 错误 | 后果 | 解决方案 |
|---|---|---|
| 未清除定时器 | 内存泄漏/状态更新后执行过期操作 | useEffect返回清理函数 |
| 直接防抖onChange | 输入卡顿,状态更新延迟 | 状态立即更新,防抖API调用 |
| 忽略竞态条件 | 后发请求先返回导致结果错乱 | 使用AbortController取消请求 |
扩展知识
- 节流(throttle):固定时间间隔执行(适合滚动事件)
- 自定义Hook:封装可复用的防抖逻辑
function useDebounce(value, delay) { const [debouncedValue, setDebouncedValue] = useState(value); useEffect(() => { const timer = setTimeout(() => { setDebouncedValue(value); }, delay); return () => clearTimeout(timer); }, [value, delay]); return debouncedValue; } - 性能监控:使用React DevTools分析渲染次数