React学习笔记[6]✨~React18中的生命周期👻

1,987 阅读4分钟

我正在参加「掘金·启航计划」

生命周期是一个抽象的概念,在生命周期的整个过程,分成了很多个阶段:

比如挂载阶段(Mount),组件第一次在DOM树中被渲染的过程;

比如更新过程(Update),组件状态发生变化,重新更新渲染的过程;

比如卸载过程(Unmount),组件从DOM树中被移除的过程;

React内部为了告诉我们当前处于哪些阶段,会对我们组件内部实现的某些函数进行回调,这些函数就是生命周期函数:

  • 比如实现componentDidMount函数:组件已经挂载到DOM上时,就会回调;
  • 比如实现componentDidUpdate函数:组件已经发生了更新时,就会回调;
  • 比如实现componentWillUnmount函数:组件即将被移除时,就会回调;

因此我们可以在这些回调函数中编写自己的逻辑代码,来完成自己的需求功能;

在React中生命周期存在于类组件中,函数组件是没有生命周期的:

  • 但是函数组件可以通过hooks来模拟生命周期的回调(后续讲解)

1、常用生命周期

挂载阶段

  1. 在挂载阶段,即创建组件实例的时候会先执行组件的constructor方法
  • 如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数

constructor中通常只做两件事情:

  • 通过给 this.state 赋值对象来初始化内部的state
  • 为事件绑定实例(this)
  1. 紧接着会执行组件的render方法
  2. 最后会执行 componentDidMount 方法, 该方法会在组件挂载后(插入 DOM 树中)立即调用
  • 依赖于DOM的操作可以在这里进行
  • 此处是发送网络请求最好的地方(官方建议)
  • 可以在此处添加一些订阅(会在componentWillUnmount取消订阅)

Hello.jsx

import React from 'react';

export default class Hello extends React.Component{
  constructor() {
    super()
    console.log('hello constructor')
  }
  render() {
    console.log('hello render')
    return (
      <h1>hello</h1>
    )
  }
}

App.jsx

import React from 'react'
import Hello from './Hello'

export default class App extends React.Component {
  render() {
    return (
      <div>
        <Hello />
        <Hello />
      </div>
    )
  }
}

可见如果使用多次组件实例,该组件每次挂载时都会先执行constructor方法再执行render方法

如果在class组件中定义了componentDidMount函数,那么当组件挂载完毕后会被回调:

import React from 'react';

export default class Hello extends React.Component{
  constructor() {
    super()
    console.log('hello constructor')
  }
  render() {
    console.log('hello render')
    return (
      <h1>hello</h1>
    )
  }
  componentDidMount() {
    console.log('componentDidMount...')
  }
}

更新阶段

一旦执行了this.setState方法就会触发组件的更新:

  1. 此时会立即执行组件的render方法
  2. 在组件更新完后componentDidUpdate会被立即调用
import React from 'react';

export default class Hello extends React.Component{
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
    console.log('hello constructor')
  }
  changeText() {
    this.setState({ message: 'hello react'})
  }
  render() {
    console.log('hello render')
    const {message} = this.state
    return (
      <div>
        <h1>{message}</h1>
        <button onClick={() => this.changeText()}>修改</button>
      </div>
    )
  }
  componentDidMount() {
    console.log('componentDidMount...')
  }
  componentDidUpdate() {
    console.log('componentDidUpdate...')
  }
}

卸载阶段

componentWillUnmount 方法会在组件卸载及销毁之前直接调用 :

  • 在此方法中执行必要的清理操作
  • 例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等

Hello.jsx

import React from 'react';

export default class Hello extends React.Component{
  constructor() {
    super()
    this.state = {
      message: 'hello world'
    }
    console.log('hello constructor')
  }
  render() {
    console.log('hello render')
    const {message} = this.state
    return (
      <div>
        <h1>{message}</h1>
      </div>
    )
  }
  componentDidMount() {
    console.log('componentDidMount...')
  }
  componentWillUnmount() {
    console.log('componentWillUnmount...')
  }
}

App.jsx

import React from 'react'
import Hello from './Hello'

export default class App extends React.Component {
  constructor() {
    super()
    this.state = {
      isShow: true
    }
  }
  changeShow() {
    this.setState({ isShow: !this.state.isShow})
  }
  render() {
    return (
      <div>
        <button onClick={() => this.changeShow()}>切换</button>
        { this.state.isShow && <Hello />}
      </div>
    )
  }
}

2、不常用生命周期

挂载阶段

在挂载阶段,执行完毕constructor后还会执行 static getDerivedStateFromProps() 方法,执行顺序依次为:

  • constructor()
  • static getDerivedStateFromProps()
  • render()
  • componentDidMount()

static getDerivedStateFromProps(props, state)

该方法使用场景比较罕见,如果state的值在任何时候都依赖于props时才使用此方法

  • 该方法会在render前被调用,并且在初始挂载及后续更新时都会被调用
  • 该方法会返回一个对象来更新state,如果返回null则不更新任何内容
  • 该方法的存在只有一个目的:让组件在props变化时来更新state

更新阶段

在更新阶段会依次执行:

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

shouldComponentUpdate(nextProps, nextState)

当props或state发生变化时,该方法会在渲染之前被调用,返回值默认是true,如果返回false则不重新渲染组件。

  • 该方法常用于性能优化,不要企图依靠此方法来阻止渲染,因为这可能会产生bug
  • 如果返回了false则不会执行render、componentDidUpdate

App.jsx

import React from "react"
import HelloWorld from "./HelloWorld"

class App extends React.Component {

  render() {
    return (
      <div>
        <HelloWorld/>
      </div>
    )
  }
}

export default App

HelloWord.jsx

import React from "react"

class HelloWorld extends React.Component {
  constructor() {
    console.log("constructor...")
    super()

    this.state = {
      message: "Hello World"
    }
  }

  changeText() {
    this.setState({ message: "Hello React" })
  }

  render() {
    console.log("render...")
    const { message } = this.state

    return (
      <div>
        <h2>{message}</h2>
        <button onClick={e => this.changeText()}>修改文本</button>
      </div>
    )
  }

  componentDidMount() {
    console.log("componentDidMount...")
  }

  componentDidUpdate() {
    console.log("componentDidUpdate...")
  }

  shouldComponentUpdate() {
    return true
  }
}

export default HelloWorld

如果showComponent()返回了true,在点击按钮修改文本时:

如果showComponent()返回了false,在点击按钮修改文本时:

  shouldComponentUpdate() {
    return false
  }

getSnapshotBeforeUpdate(prevProps, prevState)

该方法会在componentDidUpdate之前被调用,可以用来在组件发生变更前从DOM中捕获一些信息(如滚动位置)。

  • 该方法返回的内容会作为参数传递给componentDidUpdate
import React from "react"

class HelloWorld extends React.Component {
  constructor() {
    console.log("constructor...")
    super()

    this.state = {
      message: "Hello World"
    }
  }

  changeText() {
    this.setState({ message: "Hello React" })
  }

  render() {
    console.log("render...")
    const { message } = this.state

    return (
      <div>
        <h2>{message}</h2>
        <button onClick={e => this.changeText()}>修改文本</button>
      </div>
    )
  }

  componentDidMount() {
    console.log("componentDidMount...")
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("componentDidUpdate...", prevProps, prevState, snapshot)
  }

  getSnapshotBeforeUpdate() {
    console.log('getSnapshotBeforeUpdate...')
    return {
      scrollTop: 100
    }
  }
}

export default HelloWorld