欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

React里的生命周期方法钩子详解

最编程 2024-02-11 22:13:23
...

前言

所谓的生命周期就是指某个事物从开始到结束的各个阶段,就好像是把人的出生到死亡分成一个个阶段,你肯定是在出生阶段起名字,而不会在成年或者死亡的阶段去起名字。当然在 React.js 中指的是组件从创建到销毁的过程,React 实例从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 React 的生命周期,各个阶段有相对应的事件钩子,用户可以在特定的阶段调用特定的方法。每个阶段组件内部的属性都是不一样的,所以一般特定的钩子做特定的事。

一、React生命周期图

【1】过去

【2】现在

二、React生命周期演变

这么多生命周期钩子,实际上总结起来只有三个过程:挂载、更新、卸载,挂载和卸载只会执行一次,更新会执行多次。

一个完整的React组件生命周期会依次调用如下钩子

【1】生命周期演变

React版本 挂载阶段 更新阶段 卸载阶段
之前(React 16.3 之前) constructor componentWillMount render componentDidMount componentWillReceiveProps shouldComponentUpdate componentWillUpdate render componentDidUpdate componentWillUnmount
现在 constructor getDerivedStateFromProps render componentDidMount getDerivedStateFromProps shouldComponentUpdate render getSnapshotBeforeUpdate componentDidUpdate componentWillUnmount

【2】废弃和新增

原来(React v16.0前)的生命周期在React v16推出的Fiber之后就不合适了,因为如果要开启async rendering,在render函数之前的所有函数,都有可能被执行多次。所以除了shouldComponentUpdate,其他在render函数之前的所有函数(componentWillMount,componentWillReceiveProps,componentWillUpdate)都被getDerivedStateFromProps替代。

以下生命周期钩子将被逐渐废弃,看出特点了么,都是带有will的钩子

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

引入了以下两个生命周期钩子

  • getDerivedStateFromProps

  • getSnapshotBeforeUpdate

【3】注意

  1. getDerivedStateFromProps前面要加上static保留字,声明为静态方法,不然会被react忽略掉

  2. getDerivedStateFromProps里面的this为undefined

三、React生命周期方法介绍

【1】constructor(props):初始化钩子函数

constructor()中完成了React数据的初始化,它接受两个参数:props和context,当想在函数内部使用这两个参数时,需使用super()传入这两个参数。如果没有初始化状态(state),并且没有绑定方法,通常不需要为 React 组件实现一个构造函数。

注意:

  1. 只要使用了constructor()就必须写super(),否则会导致this指向错误。
  2. 不需要在构造函数中调用 setState(),只需将初始状态设置给 this.state 即可 。

React 构造函数通常只用于两个目的:

  1. 通过分配一个对象到this.state来初始化本地state
  2. 将事件处理程序方法绑定到实例
constructor(props) {
 super(props);
 // 不要这样做
 this.state = { color: props.color };
}

【2】componentWillMount():组件将要挂载时触发的函数

这是组件挂载到DOM之前的生命周期钩子。componentWillMount()一般用的比较少,它更多的是在服务端渲染时使用。它代表的过程是组件已经经历了constructor()初始化数据后,但是还未渲染DOM时。

【3】render():组件挂载时触发的函数

render函数是类组件中唯一必须的方法,其余生命周期不是必须要写。 组件渲染时会走到该生命周期,展示的组件都是由 render() 生命周期的返回值来决定。render函数会插入jsx生成的dom结构,react会生成一份虚拟dom树,在每一次组件更新时,在此react会通过其diff算法比较更新前后的新旧DOM树,比较以后,找到最小的有差异的DOM节点,并重新渲染。

【4】componentDidMount():组件挂载完成时触发的函数

这是组件挂载到DOM之后的生命周期钩子。组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

【5】componentWillUnmount ():组件将要销毁时触发的函数

这是组件卸载之前的生命周期钩子,在此处完成组件的卸载和数据的销毁。

【6】componentWillReceiveProps (nextProps):父组件中改变了props传值时触发的函数

  1. 在接受父组件改变后的props需要重新渲染组件时用到的比较多
  2. 接受一个参数nextProps
  3. 通过对比nextProps和this.props,将nextProps的state为当前组件的state,从而重新渲染组件
  componentWillReceiveProps (nextProps) {
    nextProps.isOpen !== this.props.isOpen && this.setState({
        isOpen:nextProps.isOpen
    }, () => {
      //将state更新为nextProps,在setState的第二个参数(回调)可以打印出新的state
  })
}

【7】shouldComponentUpdate(nextProps,nextState):是否要更新组件时触发的函数

  1. 这个生命周期钩子是一个开关,判断是否需要更新,主要用来优化性能(部分更新),如果开发者调用this.forceUpdate强制更新,React组件会无视这个钩子。

  2. 对于组件来说,只有状态发生改变,才需要重新渲染。所以shouldComponentUpdate生命周期钩子暴露了两个参数,开发者可以通过比较this.props和nextProps、this.state和nextState来判断状态到底有没有发生改变,再相应的返回true或false。

  3. 唯一用于控制组件重新渲染的生命周期,由于在react中,setState以后,state发生变化,组件会进入重新渲染的流程,在这里return false可以阻止组件的更新

  4. 因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断

【8】componentWillUpdate (nextProps,nextState):将要更新组件时触发的函数

shouldComponentUpdate生命周期钩子返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。

【9】componentDidUpdate(prevProps,prevState):组件更新完成时触发的函数

这是组件更新之后触发的生命周期钩子,组件更新完毕后,react只会在第一次初始化成功会进入componentDidmount,之后每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state。

【10】static getDerivedStateFromProps(nextProps,prevState):静态方法生命周期钩子

getDerivedStateFromProps 会在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个对象来更新 state,如果返回 null 则不更新任何内容。为什么getDerivedStateFromProps生命周期钩子要设计成静态方法呢?因为这样开发者就访问不到this也就是实例了,也就不能在里面调用实例方法或者setsState了,用一个静态函数getDerivedStateFromProps来取代被废弃的其他几个生命周期函数,就是强制开发者在render之前只做无副作用的操作,而且能做的操作局限在根据props和state决定新的state 而已。

【11】 getSnapshotBeforeUpdate(prevProps,prevState):保存状态快照

它是用来代替componentWillUpdate生命周期钩子函数的,常见的 componentWillUpdate 的用例是在组件更新前,读取当前某个 DOM 元素的状态,并在 componentDidUpdate 中进行相应的处理。

这两者的区别在于:

  1. 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在
    componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。
  2. getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说在 getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()。

四、实例

【1】挂载阶段

创建一个子组件,代码如下,在父组件引入渲染即可

import React ,{Component} from 'react'

class Child extends Component{
    // 初始化
    constructor(props){
        console.log('01构造函数')       
        super(props)
        this.state={}
    }
    // 组件将要挂载时候触发的生命周期函数
    componentWillMount(){
        console.log('02组件将要挂载')
    }
    // 组件挂载完成时候触发的生命周期函数
    componentDidMount(){
        console.log('04组件挂载完成')
    }
    // 组件挂载时触发的生命周期函数
    render(){
        console.log('03数据渲染render')
        return(
            <div>Child组件</div>
        ) 
    }
}
export default Child

控制台打印结果顺序如下:

01构造函数
02组件将要挂载
03数据渲染render
04组件挂载完成

【2】更新阶段

数据更新的话第一步是shouldComponentUpdate确认是否要更新数据,当这个函数返回的是true的时候才会进行更新,并且这个函数可以声明两个参数nextProps和nextState,nextProps是父组件传给子组件的值,nextState是数据更新之后值,这两个值可以在这个函数中获取到。第二步当确认更新数据之后componentWillUpdate将要更新数据,第三步依旧是render,数据发生改变render重新进行了渲染。第四步是componentDidUpdate数据更新完成。

import React, { Component } from 'react'

class Child extends Component {
  constructor(props) {
    super(props)
    this.state = {
      msg: '我是一个msg数据'
    }
  }

  //是否要更新数据,如果返回true才会更新数据
  shouldComponentUpdate(nextProps, nextState) {
    console.log('01是否要更新数据')
    return true;                //返回true,确认更新
  }
  //将要更新数据的时候触发的生命周期函数
  componentWillUpdate() {
    console.log('02组件将要更新')
  }
  //更新完成时触发的生命周期函数
  componentDidUpdate() {
    console.log('04组件更新完成')
  }
  //更新数据方法
  setMsg() {
    this.setState({
      msg: '我是改变后的msg数据'
    })
  }
  render() {
    console.log('03数据渲染render')
    return (
      <div>
        {this.state.msg}
        <button onClick={() => this.setMsg()}>点我更新msg的数据</button>
      </div>
    )
  }
}
export default Child

点击按钮更新数据,控制台打印结果顺序如下:

01是否要更新数据
02组件将要挂载
03数据渲染render
04组件更新完成

五、参考

React官网: https://zh-hans.reactjs.org/

文章每周持续更新,可以微信搜索「 前端大集锦 」第一时间阅读,回复【视频】【书籍】领取200G视频资料和30本PDF书籍资料