🚨别再滥用 useEffect 了!90% React Bug 的根源就在这

你有没有发现一个现象:

  • 只要写 React,就离不开 useEffect
  • 数据变了 → 加 useEffect
  • 不知道逻辑放哪 → 塞 useEffect
  • 页面不更新 → 再加一层 useEffect

写到最后:

  • 组件里一半代码都是 useEffect
  • 无限循环、重复请求、莫名其妙重渲染、闭包陷阱满天飞
  • 改 Bug 比写功能还累

这篇文章只讲一件事:
useEffect 到底是什么?以及它为什么被 90% 的人用错?

先讲背景:useEffect 到底是干嘛的?

早期 React 组件,有一堆生命周期: componentDidMount、componentDidUpdate、componentWillUnmount... 逻辑散得到处都是,维护巨痛苦。

Hook 出来后,React 想解决一个问题:

把"跟渲染无关、跟外部交互"的逻辑,统一收拢。

于是有了 useEffect。

它的定位非常清晰:处理副作用(Side Effect)。

什么是副作用?就是跳出 React 渲染逻辑、去跟外部打交道的操作:

  • 请求 API 接口
  • 操作真实 DOM(比如聚焦第三方库)
  • 定时器、延时
  • 局事件监听(resize、keydown)
  • 本地存储、document.title
  • 同步外部系统(日志、埋点)

一句话总结:只有需要和"外部世界"同步时,才需要 useEffect。

致命误解:你把它当成了 "监听器"?

这是 React 新手最大的误区。

你以为它是:监听某个变量变化,然后执行逻辑。 但 React 的核心模型是:UI = f (state)(纯函数)

请死死记住这句话:useEffect 不是 "监听变量变化",而是 "处理副作用"。

一旦滥用,React 内部发生了什么?

你写了一个逻辑,React 执行了一条死循环:

render (渲染) → effect (执行副作用) → setState (更新状态) → render (再次渲染) → effect ...

你以为只写了几行代码,其实你在 React 里开了一条高速公路,车多了自然堵车。

滥用 useEffect 的三大灾难

  1. 多余渲染暴增(性能杀手):一次逻辑触发多次渲染,页面卡顿、掉帧。
  2. 依赖链混乱(Bug 温床):依赖数组稍微不严谨,就陷入无限循环,或者闭包陷阱数据对不上。
  3. 逻辑碎片化(维护灾难):一个功能拆碎在多个不同的 useEffect 里,逻辑碎片化,谁敢动?

典型灾难链:

A 改 B → B 改 C → C 再改 A

你以为你在写逻辑,其实你在堆 Bug。

这 4 种场景,绝对别用 useEffect

1. 计算状态 → 直接算,别存状态

js 复制代码
// ❌ 错误:多此一举,引发重复渲染 
const [a, setA] = useState(1) 
const [b, setB] = useState(0) 

useEffect(() => { 
  setB(a * 2) 
}, [a])
js 复制代码
// ✅ 正确:直接计算
const a = 1 
const b = a * 2 // 直接计算

能通过现有状态直接算出来的,就不要单独存状态,避免多余的渲染和逻辑。

2. 交互逻辑 → 写在事件处理函数里,不是 useEffect

js 复制代码
// ❌ 错误:为了弹个提示,监听整个count
useEffect(() => {
  if (count === 10) alert('够了')
}, [count])
js 复制代码
// ✅ 正确:点击时直接判断
const handleClick = () => {
  const newCount = count + 1
  setCount(newCount)
  if (newCount === 10) alert('够了')
}

用户主动触发的行为,不属于副作用同步,理应写在对应的事件处理函数中。

3. 初始化数据 → useState 初始值就能搞定

js 复制代码
// ❌ 错误:多一次render
const [user, setUser] = useState(null)
useEffect(() => {
  setUser(currentUser)
}, [])
js 复制代码
// ✅ 正确:一步到位,直接初始化
const [user] = useState(currentUser)

4. Props 同步 → 直接用 props,不要本地状态+effect

js 复制代码
// ❌ 错误:典型反模式,数据来源不单一
useEffect(() => {
  setValue(props.value)
}, [props.value])
js 复制代码
// ✅ 直接用 props
const { value } = props

🎯 useEffect 的唯一合法使用场景

只记 5 种合法场景,多一个都不用:

  1. 接口请求(记得必须带 AbortController 清理)
  2. 定时器 / 延时(必须 clear)
  3. 手动操作 DOM
  4. 全局事件监听(addEventListener 必须 remove)
  5. 同步外部系统(localStorage、title、埋点)

除此之外,能不用就不用。

结尾

很多人以为问题在 useEffect,其实问题在这里:

你有没有把组件当成"纯函数"?

通俗来讲,React 组件本该是纯函数:固定的 Props 和 State,就输出固定的 UI,不掺杂多余的副作用。

滥用 useEffect 就是强行打破这个规则,在渲染中乱加状态修改、异步逻辑,才引发各种 Bug 和性能问题。

你认为呢?Vue 的 Watch 是不是也是这个道理?欢迎在评论区一起讨论 ~~

相关推荐
deli00711 分钟前
为什么最近邻路线总在绕远?2-opt 把 50 城 TSP 缩短了 20.9%
前端
guslegend16 分钟前
底层数据设计:读写分离、分库分表与热点数据隔离
前端
Southern Wind17 分钟前
AIAgent——第一章:本地 Agent 与 Vue 3 流式全栈实战
前端·javascript·vue.js
两万五千个小时25 分钟前
从零给 DSH 写一个 Webhook 通知插件
javascript·人工智能·架构
可乐鸡翅yeah_26 分钟前
HLS 业务 Referer‑Policy 网页元标签引发播放异常排错
前端·javascript·网络·ffmpeg·php·音视频
jj_ccwgw26 分钟前
从 0 到 1 搭建 pnpm + Workspaces + Turborepo Monorepo 保姆级指南
前端·node.js
智驭未来掌门人27 分钟前
AI编程时代的设计系统落地指南:从分类选型到项目实战
前端
vipxieliang29 分钟前
深入理解 JavaScript 数据类型与类型转换
前端·javascript
花间相见30 分钟前
【计算基础|网络06】HTTPS(上):加密体系与 RSA 握手
后端·面试
真夜33 分钟前
# Vue2 树形虚拟滚动组件实战:从原型踩坑到无缝替换 el-tree
javascript