React生命周期的使用与实例解读

 更新时间:2026年01月09日 14:13:25   作者:小码快撩  
这篇文章详细介绍了React组件的生命周期,包括挂载、更新、卸载和错误处理阶段,通过代码示例和详细讲解,帮助读者理解每个生命周期方法的作用和使用场景

导语

React的生命周期是指一个组件从其创建到销毁的整个过程。

在React中,组件的生命周期被划分为几个不同的阶段,每个阶段都有其特定的方法和用途。了解并正确使用React的生命周期,对于构建稳定、可维护的React应用至关重要。

1. 挂载阶段 (Mounting)

在React中,挂载阶段(Mounting)是指组件实例被创建并首次插入到DOM中的过程。这一阶段涉及到几个关键的生命周期方法。

下面我将通过代码示例和详细讲解来说明它们的作用:

import React from 'react';

class ExampleComponent extends React.Component {
  constructor(props) {
    super(props);
    // 1. 初始化state
    this.state = {
      count: props.initialCount,
      name: 'Initial Name'
    };

    // 2. 绑定实例方法
    this.handleButtonClick = this.handleButtonClick.bind(this);
  }

  // 3. 静态方法(可选)
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.name !== prevState.name) {
      return { name: nextProps.name };
    }
    // 返回null或undefined表示不需要更新state
    return null;
  }

  handleButtonClick() {
    // 处理按钮点击逻辑
    this.setState((prevState) => ({ count: prevState.count + 1 }));
  }

  componentDidMount() {
    // 4. 挂载后执行的操作
    console.log('Component did mount!');
    const timerId = setInterval(() => {
      console.log('Timer tick');
    }, 1000);

    // 建议在componentWillUnmount中清理定时器
    this.setState({ timerId });
  }

  render() {
    // 5. 渲染组件UI
    const { count, name } = this.state;
    return (
      <div>
        <h1>{name}</h1>
        <p>Count: {count}</p>
        <button onClick={this.handleButtonClick}>Increment</button>
      </div>
    );
  }
}

// 使用组件
<ExampleComponent initialCount={0} name="My Component" />

详细讲解:

  • constructor(props):构造函数在组件实例化时被调用。这里我们首先调用super(props)来继承React.Component的属性和方法。接着,我们初始化组件的state对象,通常包含那些需要跟踪并在用户交互或数据更新时改变的数据。在这个例子中,我们从传入的props中获取initialCount作为初始计数值,并设定一个初始名称'Initial Name'。
  • 绑定实例方法: 在构造函数中,我们使用.bind(this)来确保handleButtonClick方法在被触发时能正确访问到组件实例的上下文(即this)。如果不手动绑定,当这个方法在事件处理器中被调用时,可能会丢失对组件实例的引用。
  • static getDerivedStateFromProps(nextProps, prevState)(可选):这个静态方法允许我们在组件接收新props时计算新的state。它会在组件初次渲染以及后续每次props更新时被调用。在本例中,我们检查传入的name prop是否与当前state中的name不同,如果是,则返回一个新的state对象以更新name。如果不需要根据新props更新state,返回null或undefined即可。
  • componentDidMount():在组件完成首次渲染并成功插入到DOM之后,componentDidMount方法会被调用。这是执行依赖于DOM的操作(如添加事件监听器、发起Ajax请求、设置定时器等)的理想时机。在这个例子中,我们设置了定时器并将其ID保存在state中,以便在卸载时清除。
  • render():render方法是React组件的核心,必须是纯函数,不应有任何副作用。它负责根据当前的props和state返回一个React元素(通常是JSX表达式),表示组件在当前状态下的视图。在这个例子中,我们渲染了一个div,包含标题、计数显示和一个增加计数的按钮。

2. 更新阶段 (Updating)

React的更新阶段(Updating)发生在组件的props或state发生变更后,导致组件需要重新渲染。

以下是一个示例代码及详细讲解,展示了在更新阶段涉及的主要生命周期方法:

import React from 'react';

class ExampleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: props.initialCount,
      name: 'Initial Name',
      shouldRenderName: true
    };

    this.handleButtonClick = this.handleButtonClick.bind(this);
  }

  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.name !== prevState.name) {
      return { name: nextProps.name };
    }
    return null;
  }

  shouldComponentUpdate(nextProps, nextState) {
    // 仅在shouldRenderName为true且count或name发生变化时才更新
    return this.state.shouldRenderName && (nextProps.count !== this.props.count || nextState.name !== this.state.name);
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // 获取某个特定DOM节点的scrollTop值
    const node = document.getElementById('scrollableDiv');
    const scrollTop = node ? node.scrollTop : 0;
    return { scrollTop };
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot) {
      // 根据snapshot恢复滚动位置
      const node = document.getElementById('scrollableDiv');
      if (node) {
        node.scrollTop = snapshot.scrollTop;
      }
    }
  }

  handleButtonClick() {
    this.setState((prevState) => ({
      count: prevState.count + 1,
      shouldRenderName: false // 控制是否渲染name
    }));
  }

  render() {
    const { count, name } = this.state;

    return (
      <div id="scrollableDiv">
        {this.state.shouldRenderName && <h1>{name}</h1>}
        <p>Count: {count}</p>
        <button onClick={this.handleButtonClick}>Increment</button>
      </div>
    );
  }
}

// 使用组件
<ExampleComponent initialCount={0} name="My Component" />

详细讲解:

  • static getDerivedStateFromProps(nextProps, prevState)(可选):此方法在组件接收到新props或state引发重新渲染时都会被调用。与挂载阶段相同,我们检查name prop是否更改,并据此更新state。这一步骤确保即使在更新阶段,组件也能根据新的props更新内部state。
  • shouldComponentUpdate(nextProps, nextState)(可选):当props或state改变时,React默认会重新渲染组件。shouldComponentUpdate允许我们有条件地阻止不必要的渲染。在这个示例中,我们仅在shouldRenderName为true且count或name发生变化时才返回true,指示React继续渲染。否则返回false,阻止渲染。
  • getSnapshotBeforeUpdate(prevProps, prevState)(可选): 在更新后的渲染结果提交到DOM之前调用。这里我们获取一个特定DOM节点(假设有一个id为scrollableDiv的可滚动区域)的当前滚动位置。返回的对象({ scrollTop })将作为第三个参数传递给componentDidUpdate。
  • componentDidUpdate(prevProps, prevState, snapshot):更新完成且DOM已同步后调用。我们可以在这里执行依赖于DOM的操作,如使用snapshot恢复滚动位置。在本例中,如果snapshot存在,我们将滚动条位置设置回更新前的状态,防止因重新渲染导致的滚动位置丢失。
  • setState和handleButtonClick:handleButtonClick方法触发了state的更新,通过调用setState函数增加count并临时关闭shouldRenderName。这会导致组件进入更新阶段,按照上述生命周期方法执行相应的逻辑。

3. 卸载阶段 (Unmounting)

React的卸载阶段(Unmounting)发生在组件从DOM中被完全移除时。在此阶段,组件有机会执行必要的清理工作,例如取消网络请求、清除定时器、解绑事件监听器等,以避免内存泄漏和其他资源未释放的问题。

以下是卸载阶段涉及的生命周期方法代码示例及详细讲解:

import React from 'react';

class ExampleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: props.initialCount,
      name: 'Initial Name'
    };

    this.timerId = null;
    this.scrollListener = null;
  }

  componentDidMount() {
    // 设置定时器
    this.timerId = setInterval(() => {
      console.log('Timer tick');
    }, 1000);

    // 添加滚动事件监听器
    this.scrollListener = () => {
      console.log('Scroll event triggered');
    };
    window.addEventListener('scroll', this.scrollListener);
  }

  componentWillUnmount() {
    // 卸载阶段执行的清理工作
    clearInterval(this.timerId); // 取消定时器
    window.removeEventListener('scroll', this.scrollListener); // 解绑事件监听器
  }

  render() {
    const { count, name } = this.state;

    return (
      <div>
        <h1>{name}</h1>
        <p>Count: {count}</p>
      </div>
    );
  }
}

// 使用组件
const App = () => {
  const [showComponent, setShowComponent] = React.useState(true);

  return (
    <div>
      {showComponent && <ExampleComponent initialCount={0} name="My Component" />}
      <button onClick={() => setShowComponent(false)}>Remove Component</button>
    </div>
  );
};

export default App;

详细讲解:

componentDidMount():在挂载阶段(Mounting)介绍过的componentDidMount方法中,我们设置了定时器和添加了滚动事件监听器。这些都是需要在卸载时清理的资源。

timerIdscrollListener:我们在组件类的实例字段中存储了定时器ID(timerId)和滚动事件监听器回调函数(scrollListener)。这样在卸载时就能方便地访问到这些资源进行清理。

componentWillUnmount():当组件即将从DOM中卸载时,componentWillUnmount方法会被调用。在这个方法中,我们需要执行所有必要的清理工作以防止内存泄漏和资源浪费。具体如下:

  • clearInterval(this.timerId):使用clearInterval函数取消在componentDidMount中设置的定时器,停止其持续运行并释放相关资源。
  • window.removeEventListener('scroll', this.scrollListener):使用removeEventListener函数移除在componentDidMount中添加的滚动事件监听器。这样当组件卸载后,浏览器不会再触发该监听器,避免了无谓的函数调用和潜在的内存泄漏。

组件的动态展示与隐藏:在上面的App组件中,我们使用了React的useState Hook来管理一个布尔值showComponent,决定是否渲染ExampleComponent。

当用户点击“Remove Component”按钮时,setShowComponent(false)会导致ExampleComponent不再渲染,从而触发其卸载阶段的生命周期方法。

4. 错误处理阶段 (Error Handling)

React中的错误处理主要通过所谓的“错误边界”(Error Boundaries)来实现。错误边界是一种特殊的React组件,它可以捕获并处理其子组件树中任意位置发生的JavaScript错误,并且在出现错误时提供优雅降级的用户体验。

以下是一个错误边界组件的代码示例及详细讲解:

import React, { Component } from 'react';

class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, errorMessage: '' };
  }

  static getDerivedStateFromError(error) {
    // 更新 state 使下一次渲染能够显示降级后的 UI
    return { hasError: true, errorMessage: error.message };
  }

  componentDidCatch(error, errorInfo) {
    // 你可以将错误日志上报给服务器
    logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // 你可以自定义降级后的 UI 并显示错误信息
      return <h1>Something went wrong:</h1> <pre>{this.state.errorMessage}</pre>;
    }

    return this.props.children; // 通常情况下,渲染子组件
  }
}

function logErrorToMyService(error, errorInfo) {
  // 实现向服务器发送错误报告的逻辑
  console.error('An error occurred:', error, errorInfo);
}

function AppContent() {
  // 假设此处可能存在引发错误的代码
  // ...
  throw new Error('A simulated error in a child component.');
}

function App() {
  return (
    <div>
      <h1>Welcome to my app</h1>
      <ErrorBoundary>
        <AppContent />
      </ErrorBoundary>
    </div>
  );
}

export default App;

详细讲解:

  • ErrorBoundary 类:创建一个名为 ErrorBoundary 的React组件类,它继承自 Component。此类将充当错误边界的角色。
  • state 初始化:在构造函数中,初始化state,包含两个字段:hasError(用于标记是否发生了错误)和errorMessage(用于存储捕获到的错误信息)。
  • static getDerivedStateFromError(error):这是一个静态生命周期方法,当其后代组件树中抛出错误时会被调用。这里我们更新state,将hasError设为true,并将error.message赋值给errorMessage。这样在下次渲染时,组件可以展示一个降级后的UI,而不是崩溃的子组件树。
  • componentDidCatch(error, errorInfo):这是另一个用于错误处理的生命周期方法。当错误被捕获时,此方法会被调用,同时传递error对象(包含错误信息)和errorInfo对象(包含更详细的堆栈信息)。在这个方法中,我们可以执行诸如将错误信息上报到服务器、记录日志等操作。示例中简单地使用console.error打印错误信息。
  • render 方法:根据state.hasError的值来决定渲染的内容。若hasError为true(意味着发生了错误),则渲染一个简单的错误提示UI,包括错误消息。否则,正常渲染传入的子组件(this.props.children),即AppContent。
  • logErrorToMyService 函数:这是一个虚构的函数,代表了将错误信息上报到服务器或其他日志服务的实际逻辑。在实际项目中,您可以替换为使用如 Sentry、Bugsnag 或您自己的日志服务。
  • AppContentApp 组件:AppContent 是一个模拟可能引发错误的子组件。这里我们故意抛出一个错误,以演示错误边界的运作。App 组件则是应用的根组件,它包裹AppContent在一个ErrorBoundary组件内,这样当AppContent内部发生错误时,ErrorBoundary就能捕获并处理这些错误。

总结

了解并正确使用React的生命周期,对于构建稳定、可维护的React应用具有重要意义。

在实际开发中,我们需要根据需求合理选择生命周期方法,并在合适的时机执行相应的操作。同时,我们还需要注意React版本的变化,因为不同版本的React可能会对生命周期方法进行一些调整。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • 在React框架中实现一些AngularJS中ng指令的例子

    在React框架中实现一些AngularJS中ng指令的例子

    这篇文章主要介绍了在JavaScript的React框架中实现一些AngularJS指令的例子,React使用Virtual DOM因而与普通的js框架有些不同,需要的朋友可以参考下
    2016-03-03
  • react进阶教程之异常处理机制error Boundaries

    react进阶教程之异常处理机制error Boundaries

    在react中一旦出错,如果每个组件去处理出错情况则比较麻烦,下面这篇文章主要给大家介绍了关于react进阶教程之异常处理机制error Boundaries的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • 在React中写一个Animation组件为组件进入和离开加上动画/过度效果

    在React中写一个Animation组件为组件进入和离开加上动画/过度效果

    这篇文章主要介绍了在React中写一个Animation组件为组件进入和离开加上动画/过度效果,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-06-06
  • 详解Webpack+Babel+React开发环境的搭建的方法步骤

    详解Webpack+Babel+React开发环境的搭建的方法步骤

    本篇文章主要介绍了详解Webpack+Babel+React开发环境的搭建的方法步骤,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • React超详细讲述Fiber的使用

    React超详细讲述Fiber的使用

    在fiber出现之前,react的架构体系只有协调器reconciler和渲染器render。当前有新的update时,react会递归所有的vdom节点,如果dom节点过多,会导致其他事件影响滞后,造成卡顿。即之前的react版本无法中断工作过程,一旦递归开始无法停留下来
    2023-02-02
  • react组件封装input框的防抖处理的项目实现

    react组件封装input框的防抖处理的项目实现

    本文主要介绍了react组件封装input框的防抖处理的项目实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • React合成事件详解

    React合成事件详解

    这篇文章主要介绍了React合成事件的相关资料,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-05-05
  • react express实现webssh demo解析

    react express实现webssh demo解析

    这篇文章主要为大家介绍了详解react express实现webssh demo解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-04-04
  • React纯前端模拟实现登录鉴权

    React纯前端模拟实现登录鉴权

    这篇文章主要为大家详细介绍了React纯前端模拟实现登录鉴权的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-04-04
  • React Fiber构建beginWork源码解析

    React Fiber构建beginWork源码解析

    这篇文章主要为大家介绍了React Fiber构建beginWork源码解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02

最新评论