跳到主要内容

useEffect

面试速答(30 秒版 TL;DR)

  • useEffect 用于处理 副作用(side effects),也就是“渲染完成后再做的事”。
  • 常见副作用有:发请求、订阅事件、开启定时器、操作非 React 管理的外部系统。
  • React 18 里要重点记两句:
    • Effect 默认在提交到 DOM 之后执行
    • 开发环境 StrictMode 下会额外执行一次挂载清理流程来帮你发现副作用问题
  • 面试里不要把 useEffect 说成“生命周期翻版”,它更准确是“把渲染和副作用同步起来”的机制。

1. 什么叫副作用

所谓副作用,就是“不只是根据 props/state 计算 UI”的行为,例如:

  • 请求接口
  • 订阅 WebSocket / DOM 事件
  • 操作定时器
  • 手动改标题 document.title

这些事情不能直接塞进 render,因为 render 应该尽量保持纯。

2. 基本用法

useEffect(() => {
const id = setInterval(() => {
console.log('tick')
}, 1000)

return () => clearInterval(id)
}, [])

可以把它拆成两部分理解:

  • effect 函数:建立副作用
  • cleanup 函数:清理副作用

3. 依赖数组怎么理解

3.1 不传依赖数组

useEffect(() => {
console.log('每次渲染后都执行')
})

每次提交后都执行。

3.2 传空数组 []

useEffect(() => {
console.log('初次挂载后执行')
}, [])

语义上接近“挂载后执行一次”,但 React 18 开发环境的 StrictMode 里会额外做一次检查流程,所以不要把它机械等同于传统 componentDidMount

3.3 传具体依赖

useEffect(() => {
fetchUser(userId)
}, [userId])

只有依赖变化后,effect 才会重新同步。

4. React 18 的关键变化和面试点

4.1 StrictMode 开发环境双调用

React 18 开发环境下,挂载阶段可能经历:

  1. 执行 effect
  2. 立刻 cleanup
  3. 再执行一次 effect

目的不是线上重复执行,而是帮助你发现:

  • cleanup 不完整
  • 重复订阅
  • 非幂等副作用

4.2 不要用它“推导状态”

下面这种写法常常是不必要的:

useEffect(() => {
setFullName(firstName + lastName)
}, [firstName, lastName])

如果值能直接从当前 state/props 计算出来,优先在 render 阶段直接算,而不是再开一个 effect。

5. 常见场景

5.1 请求数据

useEffect(() => {
let cancelled = false

async function load() {
const data = await fetchUser(userId)
if (!cancelled) setUser(data)
}

load()
return () => {
cancelled = true
}
}, [userId])

5.2 订阅与解绑

  • DOM 事件
  • WebSocket
  • 第三方库实例

核心原则:建立什么,就清理什么。

6. 面试高频答法

Q1:useEffectuseLayoutEffect 的区别?

  • useEffect:DOM 提交后,通常在浏览器绘制之后异步执行,更适合大多数副作用
  • useLayoutEffect:DOM 提交后、浏览器绘制前同步执行,适合测量布局或避免闪烁

Q2:为什么依赖数组里要写全?

因为 effect 要和它用到的响应式值保持同步。依赖漏写,最常见的问题就是:

  • 闭包读到旧值
  • 副作用没有按预期更新

Q3:为什么说“能不用 effect 就不用”?

因为 effect 是和外部系统同步的手段,不是通用业务逻辑收纳箱。很多“状态推导”或“事件响应”其实可以直接写在:

  • render 计算中
  • 事件处理函数里

常见追问

1)cleanup 什么时候执行?

  • 下一次 effect 执行前
  • 组件卸载时

2)请求一定要写在 useEffect 吗?

客户端拉取场景通常会放在 effect,但框架化场景下也可能前移到 loader、server component、路由层数据获取。

易错点/坑

  • useEffect 当“生命周期替代品”机械背诵。
  • 用 effect 推导纯计算状态。
  • 依赖漏写,导致闭包陈旧。
  • 忘记 cleanup,造成重复订阅和内存泄漏。

速记要点(可背诵)

  • useEffect:渲染完成后同步外部副作用。
  • React 18 开发环境 StrictMode 会额外跑一轮挂载清理流程。
  • 原则:能在 render 算的别进 effect,建立副作用就要成对清理。