React新旧生命周期详解:从原理到实践
一、React生命周期概述
React组件的生命周期是指组件从创建到卸载的整个过程,它提供了多个钩子函数让开发者能够在不同阶段执行特定操作。随着React版本的迭代,生命周期API发生了重要变化,尤其是在React 16.3版本中引入了新的生命周期方法,同时标记了部分旧方法为不安全(UNSAFE_)。
二、旧版生命周期(React 16.3之前)
1. 生命周期执行顺序
旧版生命周期主要分为三个阶段:初始化阶段、更新阶段和卸载阶段。
初始化阶段(由ReactDOM.render()触发)
更新阶段(由this.setState()或父组件render触发)
卸载阶段(由ReactDOM.unmountComponentAtNode()触发)

旧生命周期图
2.旧生命周期代码示例
点击查看旧生命周期演示
三、新版生命周期(React 16.3及以后)
1. 生命周期执行顺序
新版生命周期引入了两个新的生命周期方法,并对三个旧方法添加了UNSAFE_前缀。
挂载阶段
更新阶段
卸载阶段
新生命周期图
2. 新生命周期代码示例
点击查看新生命周期演示
派生状态的作用
派生状态主要用于以下场景:
四、为什么要弃用旧的生命周期钩子
新生命周期改动:(带Will的要改,除了componentWillUnmount)
componentWillMount → UNSAFE_componentWillMount
componentWillReceiveProps → UNSAFE_componentWillReceiveProps
componentWillUpdate → UNSAFE_componentWillUpdate
为什么更改,官网给出答案:(不是核心的钩子)
React团队在致力于实现异步渲染的过程中发现,某些旧的生命周期方法容易导致不安全的编码实践。根据官方文档,以下方法被标记为不安全:
- componentWillMount
- componentWillReceiveProps
- componentWillUpdate 这些方法被弃用的主要原因包括:
五、新生命周期方法详解
1. static getDerivedStateFromProps(props, state)
- 调用时机 :在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用。
- 返回值 :返回一个对象来更新state,如果返回null则不更新任何内容。
- 适用场景 :state的值在任何时候都取决于props的情况。
- 注意事项 :
- 这是一个静态方法,无权访问组件实例(this)
- 每次渲染前都会触发,而不仅是在props更改时
- 如果需要执行副作用(如数据提取或动画),应该使用componentDidUpdate
- 如果只需要在prop更改时重新计算某些数据,考虑使用memoization helper
- 如果想在prop更改时"重置"某些state,考虑使组件完全受控或使用key使组件完全不受控
2. getSnapshotBeforeUpdate(prevProps, prevState)
- 调用时机 :在render之后,DOM更新之前调用。
- 返回值 :返回一个快照值(任何类型),该值将作为componentDidUpdate的第三个参数。
- 适用场景 :需要在DOM更新前捕获一些信息(如滚动位置)的场景。
- 注意事项 :
- 此方法的返回值必须与componentDidUpdate配合使用
- 典型用例是在列表滚动时保持滚动位置
六、新旧生命周期对比总结
主要变化
最佳实践建议
迁移策略
- 对于componentWillMount:将其内容移至componentDidMount
- 对于componentWillReceiveProps:使用getDerivedStateFromProps替代
- 对于componentWillUpdate:使用getSnapshotBeforeUpdate替代
七、总结
React生命周期的更新反映了React团队对异步渲染的重视,以及对更安全、更可预测的组件行为的追求。虽然旧的生命周期方法仍然可以使用(带有UNSAFE_前缀),但建议新的项目采用新的生命周期API,以避免未来可能出现的兼容性问题。
理解并正确使用生命周期方法是编写高质量React组件的关键。新的生命周期API虽然增加了一些学习成本,但它们提供了更明确的职责划分和更安全的使用方式,有助于构建更健壮、更易于维护的React应用。
经典面试题
1.React组件的生命周期分为哪些阶段?各阶段包含哪些主要方法?
答案 : React组件生命周期分为三个主要阶段:
挂载阶段(Mounting) :组件从创建到首次渲染到DOM的过程
- constructor() :初始化state和绑定事件处理函数
- static getDerivedStateFromProps(props, state) :根据props更新state(React 16.3+新增)
- render() :渲染虚拟DOM
- componentDidMount() :组件挂载后调用,适合执行副作用操作(如数据请求、订阅事件)
更新阶段(Updating) :组件props或state变化时触发的重新渲染过程
- static getDerivedStateFromProps(props, state) :同上
- shouldComponentUpdate(nextProps, nextState) :决定是否重新渲染,返回布尔值
- render() :重新渲染
- getSnapshotBeforeUpdate(prevProps, prevState) :在DOM更新前获取快照(如滚动位置)
- componentDidUpdate(prevProps, prevState, snapshot) :更新后调用,可执行DOM操作或数据请求
卸载阶段(Unmounting) :组件从DOM中移除的过程
- componentWillUnmount() :执行清理操作(如取消订阅、清除定时器)
2.React 16.3+为什么废弃componentWillMount、componentWillReceiveProps和componentWillUpdate?替代方案是什么?
答案 : 废弃原因 :
替代方案 :
示例代码 :
// 替代componentWillReceiveProps的静态方法
// 作用:根据新传入的props计算并返回新的state
// 注意:这是静态方法,不能使用this关键字,必须返回对象或null
static getDerivedStateFromProps(nextProps, prevState) {
// 比较新props中的userId与当前state中的userId是否不同
if (nextProps.userId !== prevState.userId) {
// 如果不同,返回新的state对象更新userId
return { userId: nextProps.userId };
}
// 如果相同,返回null表示不需要更新state
return null;
}
// 组件更新完成后的生命周期方法
// 作用:执行依赖于DOM更新的副作用操作(如数据请求、DOM操作)
// 参数prevProps:更新前的props
componentDidUpdate(prevProps) {
// 比较当前props与更新前的props中的userId是否不同
if (this.props.userId !== prevProps.userId) {
// 如果不同,调用数据请求方法获取新用户数据
this.fetchUserData(this.props.userId); // 副作用操作(数据请求、订阅等)
}
}
评论前必须登录!
注册