React生命周期钩子函数

前言

所谓的生命周期就是指某个事物从开始到结束的各个阶段,就好像是把人的出生到死亡分成一个个阶段,你肯定是在出生阶段起名字,而不会在成年或者死亡的阶段去起名字。当然在 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书籍资料

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容