useEffect 和生命周期怎么对应
UseEffect vs Lifecycle Methods
一句话(这句要说准):useEffect不是生命周期的替代品, 它是「同步副作用」的另一种思路—— 你声明「这个副作用依赖哪些值」, 值变了它就重新跑。
| 类组件 | useEffect 写法 |
|---|---|
componentDidMount | useEffect(fn, []) |
componentDidUpdate | useEffect(fn, [dep]) |
componentWillUnmount | effect 里 return () => {} |
| 三个都要 | useEffect(fn)(不写依赖数组) |
getSnapshotBeforeUpdate | useLayoutEffect(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 component | useEffect form |
|---|---|
componentDidMount | useEffect(fn, []) |
componentDidUpdate | useEffect(fn, [dep]) |
componentWillUnmount | return () => {} inside the effect |
| All three at once | useEffect(fn) (no dependency array) |
getSnapshotBeforeUpdate | useLayoutEffect (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).