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、订阅。组件内部能算的,就留在渲染里算。


