如何避免React生命周期中的坑
面试速答
- 以下内容以 React 18 为准。
- 生命周期最容易踩坑的根因,不是“记不住名字”,而是 把副作用、状态推导、DOM 操作混在错误阶段做了。
- 最稳的原则是:
- render 保持纯
- 副作用放提交后
- 建立什么订阅,就清理什么订阅
- 不滥用派生状态
核心理解
React 18 更推荐用“渲染阶段 / 提交阶段 / 副作用同步”来理解生命周期,而不是死背类组件那一串方法名。
1. 不要在 render 阶段做副作用
render 阶段应该只做计算,不能做这些事:
- 发请求
- 改 DOM
- 订阅事件
setState形成循环
因为 render 可能被重复执行、打断、丢弃。
2. 不要滥用派生状态
典型错误:
const [fullName, setFullName] = useState('')
useEffect(() => {
setFullName(firstName + lastName)
}, [firstName, lastName])
如果值能由当前输入直接计算出来,就应该直接算,不要再存一份状态。
3. 清理逻辑一定成对出现
最常见的问题是:
addEventListener后忘记removeEventListenersetInterval后忘记clearInterval- 请求竞态没取消,旧请求覆盖新数据
React 18 开发环境 StrictMode 故意多跑一轮 effect,就是为了尽早暴露这种问题。
类组件里常见坑
1. 旧生命周期里做不安全副作用
componentWillMount、componentWillReceiveProps、componentWillUpdate 这类旧 API 已经不推荐使用。
原因是它们位于不稳定阶段,和 React 18 的更新模型不匹配。
2. 在 componentDidUpdate 里无条件 setState
如果不做条件判断,就会造成死循环。
3. getDerivedStateFromProps 滥用
很多人拿它做“同步 props 到 state”,结果带来:
- 双份数据源
- 状态不一致
- 难排查覆盖问题
函数组件里常见坑
1. useEffect 依赖写不全
会导致闭包拿到旧值,表现为:
- 定时器一直读旧状态
- 事件回调不更新
- 请求参数滞后
2. 把所有逻辑都塞进一个 effect
这会让依赖关系变乱,正确做法是按职责拆分:
- 一个 effect 处理订阅
- 一个 effect 处理请求
- 一个 effect 处理标题同步
典型题标准答法
问:如何避免生命周期坑?
答:核心是按阶段放逻辑。render 保持纯,副作用放 effect/commit 后,依赖写完整,订阅和清理成对出现,不把 props 简单镜像成 state。
易错点
- 把
useEffect([])机械等同于componentDidMount。 - 忘记 React 18 开发环境会额外执行一次挂载清理流程。
- 在更新后无条件再触发更新。
速记要点
- 纯计算留在 render,副作用放提交后。
- 少用派生状态,避免双数据源。
- 建立副作用就必须清理。