React 实践约 9 分钟

useEffect 到底什么时候执行:依赖数组与渲染的关系

把 useEffect 当成生命周期来记,很容易写出重复请求和内存泄漏。这篇笔记从渲染结果出发,说明依赖数组、清理函数与请求时机。

代码编辑器中展示 React Hooks 的 useEffect 代码片段
本文目录

useEffect 不是 afterMount,它描述的是「这次渲染的结果和依赖值绑定着一段副作用」。理解这一点,很多奇怪的重复执行都会自己解释清楚。

依赖数组决定要不要再跑

React 会在渲染提交后比较依赖数组里的每个值和上一次是否相同,全部相同就跳过,否则先执行上一次返回的清理函数,再执行新的副作用。

TSX
useEffect(() => {
  const controller = new AbortController();

  fetch(`/api/posts?category=${category}`, {
    signal: controller.signal,
  })
    .then((res) => res.json())
    .then(setPosts)
    .catch(() => {});

  return () => controller.abort();
}, [category]);

清理函数在什么时候执行

清理函数在这个副作用即将重新执行之前、以及组件卸载时各执行一次。所以它处理的是「上一次留下的东西」,包括取消请求、移除监听、清除定时器。

  • 依赖频繁变化时,请求要用 AbortController 取消
  • 事件监听要在清理函数里移除同一个引用
  • 定时器要在清理函数里 clearTimeout,避免重复叠加

什么时候不需要 useEffect

能由 props 或 state 直接算出来的值,不要在副作用里再存一份 state。这类写法会多一次渲染,也让数据出现两份来源。

副作用只处理「组件之外的东西」:网络、DOM、订阅。组件内部能算的,就留在渲染里算。

相关阅读