跳到主要内容

如何避免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 后忘记 removeEventListener
  • setInterval 后忘记 clearInterval
  • 请求竞态没取消,旧请求覆盖新数据

React 18 开发环境 StrictMode 故意多跑一轮 effect,就是为了尽早暴露这种问题。

类组件里常见坑

1. 旧生命周期里做不安全副作用

componentWillMountcomponentWillReceivePropscomponentWillUpdate 这类旧 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,副作用放提交后。
  • 少用派生状态,避免双数据源。
  • 建立副作用就必须清理。