useEffect 进阶
上一章我们把文章列表请求了回来,但搜索功能还停留在设想。真实的搜索框是"每敲一键就触发一次",若每敲一个字都去过滤或发请求,既浪费又卡顿。本章用 useEffect 的依赖数组与清理函数实现搜索防抖——这是用好 useEffect 的关键进阶。
受控输入框
把输入框的值交给 state 管理(受控组件),用户每敲一个字符,keyword 就更新一次:
const [keyword, setKeyword] = useState("");
<input
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="搜索文章标题"
/>
依赖数组:变了才执行
假设 allPosts 是全量文章,filtered 是当前展示列表。把 keyword 放进依赖数组,关键字一变,effect 就重新过滤一次:
const [filtered, setFiltered] = useState(allPosts);
useEffect(() => {
const result = allPosts.filter(
(p) => p.title.includes(keyword) || p.summary.includes(keyword)
);
setFiltered(result);
}, [keyword]); // 只依赖 keyword
注意:effect 里用到的变量都要写进依赖数组,漏写会让 effect 读到上一次的旧值。
清理函数是干什么的
effect 可以 return 一个函数,React 会在下一次执行 effect 之前先调用它"打扫战场",比如取消定时器、取消请求。没有清理函数,多个副作用就会叠加打架:
useEffect(() => {
const timer = setTimeout(() => {
console.log("300ms 后执行:" + keyword);
}, 300);
return () => clearTimeout(timer); // 下次执行前,先清掉上一个定时器
}, [keyword]);
用清理函数做防抖
把定时过滤和清理函数合起来就是防抖:连续敲字时,上一个定时器不断被取消,只有停下来 300ms 之后才会真正执行一次过滤:
useEffect(() => {
const timer = setTimeout(() => {
const result = allPosts.filter((p) => p.title.includes(keyword));
setFiltered(result);
}, 300);
return () => clearTimeout(timer);
}, [keyword]);
请求接口时同理:把过滤换成 fetch,并在清理函数里调用 controller.abort() 取消未完成的请求,还能避免慢响应回来覆盖新结果。
小结
useEffect 进阶 = 管好依赖数组 + 写好清理函数:依赖决定"什么时候重新执行",清理决定"上一次的遗留如何收尾"。防抖让搜索又快又省。下一章我们把防抖与搜索逻辑抽成自定义 Hook,让组件更清爽、逻辑可复用。