7
头图

快来加入我们吧!

"小和山的菜鸟们",为前端开发者提供技术相关资讯以及系列基础文章。为更好的用户体验,请您移至我们官网小和山的菜鸟们 ( https://xhs-rookies.com/ ) 进行学习,及时获取最新文章。

"Code tailor" ,如果您对我们文章感兴趣、或是想提一些建议,微信关注 “小和山的菜鸟们” 公众号,与我们取的联系,您也可以在微信上观看我们的文章。每一个建议或是赞同都是对我们极大的鼓励!

前言

这节我们将介绍 React 中组件的生命周期,以及和生命周期相互关联的声明周期函数。

注意:本文所介绍的生命周期为 React 17.0.1 版本,读者时期也许会有改动,最终生命周期等方法和过程请以官网为主,react 官网

本文会向你介绍以下内容:

  • 认识生命周期
  • 生命周期解析
  • 常用生命周期函数
  • 不常用生命周期

认识生命周期

很多的事物都有从创建到销毁的整个过程,这个过程称之为是生命周期

React 组件也有自己的生命周期,了解组件的生命周期可以让我们在最合适的地方完成自己想要的功能;

在组件经过不同的生命周期,就会有不同的生命周期函数被触发,让我们在最合适的地方写入逻辑。

例如:

有一个组件,在生成的时候需要调用网络请求数据,那么就应该在 componentDidMount() 方法中去请求网络。

注意: 我们谈 React 生命周期时,主要谈的类的生命周期,因为函数式组件是没有生命周期函数的;

生命周期解析

每个类组件都包含 “生命周期方法”,你可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法,我们来学习一下这些的生命周期函数,下面是官方给出的生命周期图谱:

挂载时:

  • 当我们挂载一个组件时,会先执行 constructor 构造方法来创建组件;
  • 然后会调用 getDerivedStateFromProps,它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
  • 紧接着调用 render 函数,获取要渲染的 DOM 结构(jsx),并且开始渲染 DOM
  • 当组件挂载成功(DOM 渲染完成),会执行 componentDidMount 生命周期函数;

更新时:

  • 当我们通过修改 props,或者调用 setState 修改内部状态,或者直接调用 forceUpdate 时会重新调用 render 函数,进行更新操作;
  • 当组件的 propsstate 发生变化时会触发更新。此时会调用 getDerivedStateFromProps,它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
  • 然后会调用 shouldComponentUpdate,并根据此函数的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。
  • 紧接着会调用 render 函数,随即调用 getSnapshotBeforeUpdate ,进行更新操作
  • 当更新完成时,会回调 componentDidUpdate 生命周期函数;

卸载时:

  • 当我们的组件不再使用,会被从 DOM 中移除掉(卸载);
  • 这个时候会回调 componentWillUnmount 生命周期函数;

常用生命周期函数

render

render()

render() 方法是 class 组件中唯一必须实现的方法。

render 被调用时,它会检查 this.propsthis.state 的变化并返回以下类型之一:

  • React 元素。通常通过 JSX 创建。
  • 数组或 fragments。 使得 render 方法可以返回多个元素。
  • Portals。可以渲染子节点到不同的 DOM 子树中。
  • 字符串或数值类型。它们在 DOM 中会被渲染为文本节点
  • 布尔类型或 null。什么都不渲染。

render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。

如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。

注意: 如果 shouldComponentUpdate() 返回 false,则不会调用 render()

constructor

constructor(props)

如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。

constructor 中通常只做两件事情:

  • 通过给 this.state 赋值对象来初始化内部的 state
  • 为事件绑定实例(this);
constructor(props) {
  super(props);
  // 不要在这里调用 this.setState()
  this.state = { counter: 0 };
  this.handleClick = this.handleClick.bind(this);
}

只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。

注意: 避免将 props 的值赋值给 state!这是一个常见的错误

componentDidMount

componentDidMount()

componentDidMount() 会在组件挂载后(插入 DOM 树中)立即调用。

componentDidMount 中通常进行哪里操作呢?

  • 依赖于 DOM 的操作可以在这里进行;
  • 在此处发送网络请求就最好的地方;(官方建议)
  • 可以在此处添加一些订阅(会在 componentWillUnmount 取消订阅);

componentDidUpdate

componentDidUpdate(prevProps, prevState, snapshot)

componentDidUpdate() 会在更新后会被立即调用,首次渲染不会执行此方法。

  • 当组件更新后,可以在此处对 DOM 进行操作;
  • 如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求;(例如,当 props 未发生变化时,则不会执行网络请求)。
componentDidUpdate(prevProps) {
  // 典型用法(不要忘记比较 props):
  if (this.props.userID !== prevProps.userID) {
    this.fetchData(this.props.userID);
  }
}

你也可以在 componentDidUpdate()直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。

注意: 如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()

componentWillUnmount

componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。

  • 在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等;
  • componentWillUnmount()不应调用 setState(),因为该组件将永远不会重新渲染。组件实例卸载后,将永远不会再挂载它。

代码验证上述常用的生命周期函数:

import React, { Component } from 'react'

class HYTestCpn extends Component {
  render() {
    return <h2>HYTestCpn</h2>
  }

  componentWillUnmount() {
    console.log('HYTestCpn componentWillUnmount')
  }
}

export default class App extends Component {
  constructor(props) {
    super(props)

    this.state = {
      counter: 0,
    }

    console.log('调用constructor方法')
  }

  render() {
    console.log('调用render方法')
    return (
      <div>
        <h2>当前计数: {this.state.counter}</h2>
        {this.state.counter <= 5 && <HYTestCpn />}
        <button onClick={(e) => this.increment()}>+1</button>
      </div>
    )
  }

  increment() {
    this.setState({
      counter: this.state.counter + 1,
    })
  }

  componentDidMount() {
    console.log('调用componentDidMount方法')
  }

  componentDidUpdate() {
    console.log('调用componentDidUpdate方法')
  }

  componentWillUnmount() {
    console.log('调用componentWillUnmount方法')
  }
}

不常用生命周期

除了上面介绍的常用生命周期函数之外,还有一些不常用的生命周期函数:

shouldComponentUpdate

shouldComponentUpdate(nextProps, nextState)

propsstate 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。返回值默认为 true。首次渲染或使用 forceUpdate() 时不会调用该方法。

  • 根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 stateprops 更改的影响。
  • 默认行为是 state 每次发生变化组件都会重新渲染。大部分情况下,你应该遵循默认行为。
  • 此方法仅作为性能优化的方式而存在。不要企图依靠此方法来“阻止”渲染,因为这可能会产生 bug

我们首先应该考虑使用内置的 PureComponent组件,而不是手动编写,PureComponent 会对 propsstate 进行浅层比较,并减少了跳过必要更新的可能性。

如果需要手动编写,可以将 this.propsnextProps 以及 this.statenextState 进行比较,并返回 false 以告知 React 可以跳过更新。请注意,返回 false 并不会阻止子组件在 state 更改时重新渲染。

getDerivedStateFromProps

static getDerivedStateFromProps(props, state)

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。

  • 它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。
  • getDerivedStateFromProps 的存在只有一个目的:让组件在 props 变化时更新 state,即 state 的值在任何时候都取决于 props
  • 此方法无权访问组件实例。
注意: 不管原因是什么,都会在每次渲染前触发此方法。

getSnapshotBeforeUpdate

getSnapshotBeforeUpdate(prevProps, prevState)

React 更新 DOM 之前回调的一个函数,可以获取 DOM 更新前的一些信息(比如说滚动位置);

  • 此生命周期的任何返回值将作为参数传递给 componentDidUpdate()
  • 此用法并不常见,但它可能出现在 UI 处理中,如需要以特殊方式处理滚动位置的聊天线程等。
  • 应返回 snapshot 的值(或 null)。

另外,React 中还提供了一些过期的生命周期函数,这些函数已经不推荐使用。

更详细的生命周期相关的内容,可以参考React 官网


小和山的菜鸟们
377 声望2.1k 粉丝

每日进步的菜鸟,分享前端学习手册,和有心学习前端技术的小伙伴们互相探讨,一同成长。