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

你有没有发现一个现象:

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

写到最后:

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

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

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

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

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 是不是也是这个道理?欢迎在评论区一起讨论 ~~

相关推荐
To_OC5 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
To_OC5 小时前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic6 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy6 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志6 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble7 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长8 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯8 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜9 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司9 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能