React 生命周期详解:从挂载到卸载

React 生命周期详解:从挂载到卸载

React 组件的生命周期(Lifecycle)是指组件从创建到销毁的整个过程,包括挂载、更新和卸载三个主要阶段。这套机制允许开发者在组件的不同生命周期节点注入自定义逻辑,如初始化状态、请求数据、清理资源等。

关键点

  • 主要针对类组件:React 16.3+ 引入 Hooks 后,函数组件通过 useEffect 等 Hook 模拟生命周期。但本文重点讲解类组件的经典生命周期(基于 React 18+)。
  • 已弃用方法:React 17+ 标记了 componentWillMountcomponentWillReceivePropscomponentWillUpdate 为 UNSAFE_(不推荐使用,可能在未来版本移除)。
  • 异步渲染:React 16+ 支持异步渲染,生命周期方法调用顺序可能因 Fiber 架构而略有变化,但整体流程一致。
  • Hooks 替代:函数组件更推荐,使用 useEffectuseState 等实现类似功能。

下面按时间线(从挂载到卸载)详细拆解每个阶段,包括方法调用顺序、用途、代码示例和注意事项。

1. 挂载阶段(Mounting):组件首次渲染到 DOM

这一阶段从组件实例化开始,到插入 DOM 结束。顺序固定。

方法名称调用时机用途 / 常见操作是否能访问 DOM异步渲染影响
constructor()组件实例化时(props 传入前)初始化 state、绑定方法(this.xxx = this.xxx.bind(this))
static getDerivedStateFromProps(props, state)constructor 后 / render 前(静态方法)从 props 派生 state(很少用)
render()所有准备就绪后返回 JSX / React 元素(纯函数,不能有副作用)
componentDidMount()render 后,组件已插入 DOM请求数据、添加事件监听、setState(初始化后更新)

代码示例

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.handleClick = this.handleClick.bind(this);  // 绑定方法
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.initialCount !== prevState.count) {
      return { count: nextProps.initialCount };  // 从 props 更新 state
    }
    return null;
  }

  componentDidMount() {
    // 发起 API 请求
    fetch('/api/data').then(res => this.setState({ data: res }));
    document.addEventListener('click', this.handleClick);  // 添加全局监听
  }

  render() {
    return <div>Count: {this.state.count}</div>;
  }
}

注意

  • 避免在 constructor 中调用 setState(props 还没初始化)。
  • componentDidMount 是请求数据的黄金时机(不会阻塞渲染)。
  • 已弃用:UNSAFE_componentWillMount(用 constructor 或 getDerivedStateFromProps 代替)。

2. 更新阶段(Updating):组件 props/state 变化时

当 props/state 变化、父组件重渲染或 forceUpdate() 时触发。顺序类似挂载,但多了一些更新前检查。

方法名称调用时机用途 / 常见操作是否能访问 DOM异步渲染影响
static getDerivedStateFromProps(props, state)props/state 变化后,render 前从新 props 更新 state
shouldComponentUpdate(nextProps, nextState)getDerivedStateFromProps 后性能优化:返回 false 阻止渲染(PureComponent 默认实现)
render()shouldComponentUpdate 返回 true 后返回新 JSX
getSnapshotBeforeUpdate(prevProps, prevState)render 后,DOM 更新前捕获 DOM 状态(如滚动位置)返回给 componentDidUpdate
componentDidUpdate(prevProps, prevState, snapshot)DOM 更新后更新后操作(如动画、请求新数据)

代码示例

class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.value !== this.props.value;  // 浅比较,避免不必要渲染
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 捕获滚动位置
    return document.getElementById('scroll-container').scrollTop;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.value !== prevProps.value) {
      // 根据 snapshot 恢复滚动位置
      document.getElementById('scroll-container').scrollTop = snapshot;
      // 或发起新请求
      fetch(`/api/update?newValue=${this.props.value}`);
    }
  }

  render() {
    return <div>Value: {this.props.value}</div>;
  }
}

注意

  • shouldComponentUpdate 是性能调优的关键(但不要过度使用)。
  • 已弃用:UNSAFE_componentWillReceiveProps、UNSAFE_componentWillUpdate(用 getDerivedStateFromProps / getSnapshotBeforeUpdate 代替)。
  • componentDidUpdate 避免直接 setState(防止无限循环),用条件判断。

3. 卸载阶段(Unmounting):组件从 DOM 移除

当组件被移除(父组件不渲染它)时触发。只有一个方法。

方法名称调用时机用途 / 常见操作是否能访问 DOM异步渲染影响
componentWillUnmount()组件即将从 DOM 移除前清理资源:移除事件监听、取消定时器、关闭连接

代码示例

class MyComponent extends React.Component {
  componentDidMount() {
    this.timer = setInterval(() => console.log('tick'), 1000);
    document.addEventListener('click', this.handleClick);
  }

  componentWillUnmount() {
    clearInterval(this.timer);  // 清理定时器
    document.removeEventListener('click', this.handleClick);  // 移除监听
    // 关闭 WebSocket 等
  }

  render() {
    return <div>Unmounting Example</div>;
  }
}

注意

  • 这里是清理资源的唯一时机(防止内存泄漏)。
  • 不要在这里 setState(组件已卸载)。
  • 已弃用:无(但老版本有 componentWillUnmount 的 UNSAFE_ 变体)。

4. 函数组件的生命周期(Hooks 模拟,2025 主流写法)

类组件生命周期正逐渐被 Hooks 取代。Hooks 更简洁、复用性强。

类组件方法Hooks 等价物备注
constructoruseState 初始值
getDerivedStateFromPropsuseEffect + 依赖数组
componentDidMountuseEffect(() => { … }, [])空依赖数组
shouldComponentUpdateReact.memo / useMemo
componentDidUpdateuseEffect(() => { … }, [deps])非空依赖
componentWillUnmountuseEffect(() => { return cleanup; }, [deps])返回清理函数

Hooks 示例(模拟类组件):

import { useState, useEffect } from 'react';

function MyFunctionalComponent({ value }) {
  const [count, setCount] = useState(0);

  useEffect(() => {
    // componentDidMount + componentDidUpdate
    fetch(`/api/data?value=${value}`).then(res => setCount(res.count));

    return () => {
      // componentWillUnmount
      console.log('清理资源');
    };
  }, [value]);  // 依赖 value 变化

  return <div>Count: {count}</div>;
}

注意:useEffect 的依赖数组是关键(空数组 = 只挂载时执行;无数组 = 每次渲染都执行)。

5. 常见问题与最佳实践

  • 无限循环:componentDidUpdate / useEffect 中无条件 setState → 无限渲染。
  • 性能优化:用 shouldComponentUpdate / React.memo 避免子组件不必要重渲染。
  • 异步问题:setState 是异步的(用回调形式处理)。
  • 迁移建议:新项目优先 Hooks(函数组件);老项目类组件可渐进迁移。
  • 调试工具:React DevTools 高亮组件渲染顺序和生命周期钩子。

掌握这些,你就能在 React 项目中精准控制组件行为!如果想看具体 Hooks 示例或某个方法的深度代码实战,告诉我,我们继续。

文章已创建 4138

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部