react 生命周期(保姆级教程)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

  • 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...点击查看项目介绍 ;
  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;

截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观

前言

在现代前端开发中,React 作为最受欢迎的 JavaScript 库之一,其核心特性之一是通过组件化的模式管理用户界面。而组件的 生命周期(Lifecycle)是理解 React 核心机制的关键,它定义了组件从创建到销毁的各个阶段,并允许开发者在特定时刻执行自定义逻辑。无论是数据获取、状态更新,还是资源管理,生命周期方法都是开发者与 React 框架交互的“控制台”。

本文将从零开始,深入浅出地解析 React 生命周期的原理、各阶段的作用,以及如何在实际开发中合理运用它们。无论是编程新手还是有一定经验的开发者,都能通过本文掌握这一重要概念,并解决常见问题。


一、什么是 React 生命周期?

React 生命周期可以类比为一个“电影拍摄流程”:

  • 挂载阶段(Mounting):相当于电影开机,组件首次被创建并插入到 DOM 中。
  • 更新阶段(Updating):类似拍摄中的场景调整,当组件状态(state)或属性(props)变化时触发。
  • 卸载阶段(Unmounting):如同电影杀青,组件从 DOM 中移除时执行的操作。

每个阶段对应一系列预定义的方法(如 componentDidMountcomponentDidUpdate 等),开发者通过这些方法在特定时机插入代码,实现数据加载、副作用处理(如订阅 API、定时器)等功能。


二、React 生命周期的三个核心阶段

1. 挂载阶段(Mounting)

当组件首次被渲染时,React 会依次执行以下方法:

constructor() → static getDerivedStateFromProps() → render() → componentDidMount()

关键方法详解

  • constructor()
    类组件的构造函数,用于初始化 state 和绑定事件处理函数。它是唯一可以安全修改 this.state 的地方(需通过 this.state = { ... } 赋值)。

    class Counter extends React.Component {
      constructor(props) {
        super(props);
        this.state = { count: 0 };
        this.handleClick = this.handleClick.bind(this);
      }
    }
    
  • static getDerivedStateFromProps()
    一个静态方法,允许根据 props 更新 state。它在挂载和每次更新时都会被调用,但返回 null 表示不更新。

    static getDerivedStateFromProps(nextProps, prevState) {
      if (nextProps.count !== prevState.count) {
        return { count: nextProps.count };
      }
      return null;
    }
    
  • componentDidMount()
    组件挂载到 DOM 后执行,是执行异步操作(如 API 请求、订阅事件)的黄金时机。

    componentDidMount() {
      fetch('https://api.example.com/data')
        .then(data => this.setState({ data }));
    }
    

2. 更新阶段(Updating)

当组件的 propsstate 发生变化时,React 会触发更新流程,执行以下方法:

static getDerivedStateFromProps() → shouldComponentUpdate() → render() → getSnapshotBeforeUpdate() → componentDidUpdate()

关键方法详解

  • shouldComponentUpdate(nextProps, nextState)
    决定组件是否需要重新渲染。默认返回 true,但可通过优化返回 false 来避免不必要的渲染(如性能优化)。

    shouldComponentUpdate(nextProps, nextState) {
      return nextProps.id !== this.props.id || nextState.count !== this.state.count;
    }
    
  • componentDidUpdate(prevProps, prevState, snapshot)
    在更新完成后执行,常用于副作用操作(如滚动到顶部、日志记录)。

    componentDidUpdate(prevProps) {
      if (this.props.user !== prevProps.user) {
        console.log('用户信息已更新');
      }
    }
    

3. 卸载阶段(Unmounting)

当组件从 DOM 中移除时,执行以下方法:

componentWillUnmount()

关键方法详解

  • componentWillUnmount()
    在组件销毁前清理资源(如取消订阅、清除定时器)。

    componentWillUnmount() {
      clearInterval(this.timer);
      this.unsubscribe();
    }
    

三、新旧 API 对比:Class 组件 vs. 函数组件(Hooks)

React 16.8 引入的 Hooks 彻底改变了生命周期的使用方式。以下是关键差异:

Class 组件函数组件(Hooks)
使用生命周期方法(如 componentDidMount通过 useEffect 钩子统一管理副作用
需要继承 React.Component无需继承,直接使用函数定义组件
状态管理依赖 this.state状态管理通过 useState 钩子实现

示例:用 useEffect 替代 componentDidMount

// Class 组件  
class Clock extends React.Component {
  componentDidMount() {
    this.timer = setInterval(() => this.tick(), 1000);
  }

  // ...

  componentWillUnmount() {
    clearInterval(this.timer);
  }
}

// 函数组件(Hooks)  
function Clock() {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    const timer = setInterval(() => setTime(new Date()), 1000);
    return () => clearInterval(timer); // 清理函数
  }, []); // 空依赖数组表示仅执行一次

  return <div>{time.toString()}</div>;
}

四、实战案例:生命周期在表单验证中的应用

场景描述

假设需要实现一个表单组件,要求:

  1. 在组件挂载时初始化表单数据。
  2. props 中的 formConfig 变化时,更新表单状态。
  3. 在提交前验证表单字段。

实现代码(Class 组件)

class Form extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      formData: this.props.formConfig.initialValues || {},
      errors: {},
    };
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.formConfig !== prevState.prevFormConfig) {
      return {
        formData: nextProps.formConfig.initialValues,
        prevFormConfig: nextProps.formConfig,
      };
    }
    return null;
  }

  handleSubmit = (e) => {
    e.preventDefault();
    const errors = this.validateForm();
    this.setState({ errors });
    if (Object.keys(errors).length === 0) {
      // 提交表单
    }
  };

  validateForm = () => {
    const { formData } = this.state;
    const errors = {};

    if (!formData.name) {
      errors.name = '名称不能为空';
    }
    // 其他验证逻辑...
    return errors;
  };

  componentDidUpdate(prevProps) {
    if (prevProps.formConfig !== this.props.formConfig) {
      this.setState({ formData: this.props.formConfig.initialValues });
    }
  }

  render() {
    // 渲染表单字段和错误提示
  }
}

关键点解析

  1. constructor:初始化表单数据。
  2. getDerivedStateFromProps:监听 formConfig 的变化,更新表单初始值。
  3. componentDidUpdate:进一步处理 props 变化后的逻辑(如重置表单)。

五、常见问题与最佳实践

1. 为什么不能在 render 中直接调用 API?

render 方法是纯函数,不可执行副作用操作(如 fetch)。应将这类逻辑放在 componentDidMountuseEffect 中。

2. 如何避免内存泄漏?

componentWillUnmountuseEffect 的清理函数中,务必释放所有资源(如定时器、事件监听器)。

3. 性能优化技巧

  • 通过 shouldComponentUpdateReact.memo 减少不必要的渲染。
  • 使用 useMemouseCallback 缓存计算结果或函数引用。

六、结论

React 生命周期是开发者与框架交互的核心机制,它帮助我们精确控制组件行为,从数据初始化到资源清理,每个阶段都承载着关键逻辑。无论是使用 Class 组件的传统方法,还是通过 Hooks 的现代化方式,掌握生命周期的本质逻辑,能显著提升代码的健壮性和可维护性。

随着 React Hooks 的普及,开发者可以更灵活地管理状态和副作用,但理解生命周期的基本原理,仍是构建复杂应用的基石。希望本文能助你“掌控” React 的生命周期,让代码运行得更高效、更可靠!

最新发布