云计算百科
云计算领域专业知识百科平台

为什么 React 要 “淘汰” 旧生命周期?从原理到实践,一文读懂钩子函数的迭代逻辑

React新旧生命周期详解:从原理到实践

一、React生命周期概述

React组件的生命周期是指组件从创建到卸载的整个过程,它提供了多个钩子函数让开发者能够在不同阶段执行特定操作。随着React版本的迭代,生命周期API发生了重要变化,尤其是在React 16.3版本中引入了新的生命周期方法,同时标记了部分旧方法为不安全(UNSAFE_)。

二、旧版生命周期(React 16.3之前)

1. 生命周期执行顺序

旧版生命周期主要分为三个阶段:初始化阶段、更新阶段和卸载阶段。

初始化阶段(由ReactDOM.render()触发)

  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount() ===> 常用 更新阶段(由this.setState()或父组件render触发)
  • 更新阶段(由this.setState()或父组件render触发)

  • componentWillReceiveProps()
  • shouldComponentUpdate()
  • componentWillUpdate()
  • render() ===> 必须使用
  • componentDidUpdate()
  • 卸载阶段(由ReactDOM.unmountComponentAtNode()触发)

  • componentWillUnmount() ===> 常用 在这里插入图片描述
  • 旧生命周期图

    在这里插入图片描述

    2.旧生命周期代码示例

    点击查看旧生命周期演示

    三、新版生命周期(React 16.3及以后)

    1. 生命周期执行顺序

    新版生命周期引入了两个新的生命周期方法,并对三个旧方法添加了UNSAFE_前缀。

    挂载阶段

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()
  • 更新阶段

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()
  • 卸载阶段

  • componentWillUnmount()
  • 在这里插入图片描述

    新生命周期图

    在这里插入图片描述

    2. 新生命周期代码示例

    点击查看新生命周期演示

    派生状态的作用

    派生状态主要用于以下场景:

  • 初始化状态:基于 props 初始化 state
  • 响应 props 变化:当 props 变化时更新 state
  • 数据格式化:将 props 数据转换为特定格式
  • 四、为什么要弃用旧的生命周期钩子

    新生命周期改动:(带Will的要改,除了componentWillUnmount)

    componentWillMount → UNSAFE_componentWillMount

    componentWillReceiveProps → UNSAFE_componentWillReceiveProps

    componentWillUpdate → UNSAFE_componentWillUpdate

    为什么更改,官网给出答案:(不是核心的钩子)

    React团队在致力于实现异步渲染的过程中发现,某些旧的生命周期方法容易导致不安全的编码实践。根据官方文档,以下方法被标记为不安全:

    • componentWillMount
    • componentWillReceiveProps
    • componentWillUpdate 这些方法被弃用的主要原因包括:
  • 异步渲染导致的问题 :在异步渲染模式下,这些生命周期可能会被调用多次,或者在渲染之前被取消,容易导致不一致的状态和内存泄漏。
  • 滥用风险 :这些方法经常被误解和滥用。例如,开发者可能会在componentWillMount中进行数据获取,这在服务端渲染中会导致问题,并且在异步渲染中可能被调用多次。
  • 更好的替代方案 :新的生命周期提供了更安全、更可预测的方式来实现相同的功能。getDerivedStateFromProps替代了componentWillReceiveProps,getSnapshotBeforeUpdate替代了componentWillUpdate的部分使用场景。 React团队在这些方法名称前添加了"UNSAFE_"前缀,明确表示使用这些生命周期的代码在React的未来版本中更有可能出现bug,尤其是在启用异步渲染之后。
  • 五、新生命周期方法详解

    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、componentWillReceiveProps、componentWillUpdate (仍可使用但添加了UNSAFE_前缀)
  • 新增的方法 :getDerivedStateFromProps、getSnapshotBeforeUpdate
  • 保留的方法 :constructor、render、componentDidMount、shouldComponentUpdate、componentDidUpdate、componentWillUnmount
  • 最佳实践建议

  • 数据获取 :应在componentDidMount中进行,而不是componentWillMount
  • 状态派生 :使用getDerivedStateFromProps替代componentWillReceiveProps
  • DOM操作前准备 :使用getSnapshotBeforeUpdate替代componentWillUpdate
  • 清理工作 :始终在componentWillUnmount中进行,如清除定时器、取消订阅等
  • 性能优化 :合理使用shouldComponentUpdate或React.memo避免不必要的渲染
  • 迁移策略

    • 对于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?替代方案是什么?

    答案 : 废弃原因 :

  • 异步渲染兼容性问题 :React 16引入Fiber架构支持异步渲染,这些生命周期可能在渲染过程中被多次调用,导致副作用(如数据请求)重复执行 。
  • 副作用管理风险 :开发者常在此类方法中执行数据请求或状态更新,可能导致不可预测的渲染结果 。
  • API简化 :新生命周期提供更明确的职责划分,避免滥用。
  • 替代方案 :

    在这里插入图片描述

    示例代码 :

    // 替代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); // 副作用操作(数据请求、订阅等)
    }
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 为什么 React 要 “淘汰” 旧生命周期?从原理到实践,一文读懂钩子函数的迭代逻辑
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!