DrillLab
第 61 / 105 道61 / 105 · #325

useEffect 和生命周期怎么对应

UseEffect vs Lifecycle Methods

先自己答,再往下看Answer it yourself first

一句话(这句要说准):useEffect不是生命周期的替代品, 它是「同步副作用」的另一种思路—— 你声明「这个副作用依赖哪些值」, 值变了它就重新跑。

类组件useEffect 写法
componentDidMountuseEffect(fn, [])
componentDidUpdateuseEffect(fn, [dep])
componentWillUnmounteffect 里 return () => {}
三个都要useEffect(fn)(不写依赖数组)
getSnapshotBeforeUpdateuseLayoutEffect(DOM 更新后、浏览器绘制前同步执行)

但这张表有个陷阱——useEffect(fn, [])不完全等于 componentDidMount: 前者在浏览器绘制之后异步执行,后者是同步的。所以用 useEffect测量 DOM 再改样式会闪一下, 这种情况要用useLayoutEffect

更重要的是别用生命周期的思维写 effect。正确的问法不是「我要在挂载时干什么」, 而是「这个副作用依赖哪些值」。 依赖列全,React 自然会在该跑的时候跑。

会追问:「清理函数什么时候执行?」——依赖变化前卸载时我们那道计时器变式题就是这个考点: 漏了 clearInterval, start/pause 四次会得到 10 秒而不是 4 秒(实测)。

In one line — say this precisely: useEffect is not a replacement for lifecycle methods; it is a different way of thinking about synchronising side effects — you declare which values a side effect depends on, and it re-runs when they change.

Class componentuseEffect form
componentDidMountuseEffect(fn, [])
componentDidUpdateuseEffect(fn, [dep])
componentWillUnmountreturn () => {} inside the effect
All three at onceuseEffect(fn) (no dependency array)
getSnapshotBeforeUpdateuseLayoutEffect (runs synchronously after the DOM updates, before the browser paints)

But the table has a trap useEffect(fn, []) is not quite componentDidMount: the first runs asynchronously after the browser paints, the second is synchronous. So measuring the DOM in a useEffect and then changing styles will flash; that case wants useLayoutEffect.

More important: stop writing effects with a lifecycle mindset. The right question is not “what do I do on mount” but “which values does this side effect depend on”. List the dependencies properly and React runs it when it should.

Follow-up: “When does the cleanup function run?” — before the dependencies change and on unmount. Our timer variant question tests exactly this: drop the clearInterval and four start/pause rounds give you 10 seconds instead of 4 (measured).