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,让组件更清爽、逻辑可复用。

笔记加载中…