React 生命周期(v16.0前 和 v16.4)

什么是生命周期函数?
就是不用本身手动的去调用,框架会在合适的时间自动的调用该函数react

React v16.0前 生命周期

生命周期函数分类:git

图片加载失败!

生命周期函数执行流程:
算法

图片加载失败!

初始化阶段

constructor()

constructor是ES6对类的默认方法,经过 new 命令生成对象实例时自动调用该方法(调用一次)。
而且,该方法是类中必须有的,若是没有显示定义,则会默认添加空的constructor()方法。当存在constructor的时候必须手动调用super方法。
constructor中若是要访问this.props,则须要传入propsbash

class Counter extends React.component{
    constructor(props){
        super(props); // 声明constructor时必须调用super方法
        console.log(this.props); // 能够正常访问this.props
    }
}
复制代码

constructor 经常使用来初始化state框架

constructor(props){
	super(props)
	this.state = {number:0} // 初始化的状态
}
复制代码

在类组件中也能够经过静态方法设置属性dom

class Counter extends React.component{
	static defaultProps = {name:"Fan"} // 默认属性
    constructor(props){
        super(props); // 声明constructor时必须调用super方法
        console.log(this.props); // 能够正常访问this.props
    }
}
复制代码

挂载阶段

componentWillMount()

在组件挂载以前调用,且全局只调用一次。异步

若是在这个钩子里调用this.setState不会引发组件从新渲染,也能够把写在这里的内容写道到constructor()中,因此不多使用。ide

render()

render是一个React组件必须定义的生命周期函数,用来渲染DOM。函数

并必须 return 一个React元素(描述组件,即UI),不负责组件实际渲染工做,以后由React自身根据此元素去渲染出页面DOM。
render必须是纯函数(Pure function:函数的返回结果只依赖于它的参数;函数执行过程里面没有反作用)性能

不要在render里面修改state,会触发死循环致使栈溢出。

componentDidMount()

在组件挂载完成后调用,且全局只调用一次。
能够在这里使用refs,获取真实dom元素。

该钩子内也能够发起异步请求,并在异步请求中能够进行setState

更新阶段

在讲述此阶段前须要先明确下react组件更新机制。  

setState引发的state更新或父组件从新render引发的props更新,更新后的stateprops相对以前不管是否有变化,都将引发子组件的从新render

componentWillReceiveProps(nextProps )

props发生变化以及父组件从新渲染时都会触发该生命周期函数

在该钩子内能够经过参数nextProps获取变化后的props参数,
经过this.props访问以前的props。该生命周期内能够进行setState

shouldComponentUpdate(nextProps,nextState)

组件挂载以后,每次调用setState后都会调用shouldComponentUpdate判断是否须要从新渲染组件。就是经过比较nextPropsnextState及当前组件的this.propsthis.state的状态用来判断是否须要从新渲染

默认返回true,须要从新render,返回false则不触发渲染。

通常咱们经过该函数来优化性能
例如:一个React项目须要更新一个小组件时,极可能须要父组件更新本身的状态。而一个父组件的从新更新会形成它其下全部的子组件从新执行render()方法,造成新的虚拟DOM,再用diff算法对新旧虚拟DOM进行结构和属性的比较,决定组件是否须要从新渲染
无疑这样的操做会形成不少的性能浪费,因此咱们开发者能够根据项目的业务逻辑,在shouldComponentUpdate()中加入条件判断,从而优化性能

componentWillUpdate(nextProps,nextState)

组件即将被更新时触发

shouldComponentUpdate返回true或者调用forceUpdate以后,componentWillUpdate会被调用。

不能在该钩子中setState,不然会触发重复循环。

componentDidUpdate(prevProps, prevState)

此方法在组件更新后被调用(除了首次render以后调用componentDidMount,其它render结束以后都是调用componentDidUpdate)

能够操做组件更新的DOM,prevPropsprevState这两个参数指的是组件更新前的propsstate

该钩子内setState有可能会触发重复渲染,须要自行判断,不然会进入死循环。

卸载阶段

componentWillUnmount()

此方法在组件被卸载前调用

能够在这里执行一些清理工做,好比清除组件中使用的定时器、取消Redux的订阅事件、清除componentDidMount中手动建立的DOM元素等等,以免引发内存泄漏。

测试代码

import React, { Component } from 'react';

class Father extends React.Component{
    static defaultProps = {name:"Fan"} // 默认属性
    constructor(props){
        super(props)
        this.state = {number:0} 
    }
    UNSAFE_componentWillMount(){
        console.log("Father: componentWillMount") // 1
    }
    shouldComponentUpdate(nextProps,nextState){
        console.log("Father: shouldComponentUpdate") // 
        // if(nextState.number%2 === 0){
        //     return true
        // }else{
        //     return false;
        // }
        return true;
    }
    componentWillUpdate(){
        console.log("Father: componentWillUpdate") // 
    }
    componentDidUpdate(){
        console.log("Father: componentDidUpdate") 
    }
    handleClick = ()=>{
        this.setState({number:this.state.number+1})
    }
    render(){
        console.log("Father: render")  // 2
        return(
            <div>
                <h1>父组件</h1>
                <p>{this.state.number}</p>
                <button onClick={this.handleClick}>+</button>
                <hr/>
                {this.state.number%2==0 ? <Son number={this.state.number}></Son> : null}
            </div>
        )
    }
    componentDidMount(){
        console.log("Father: componentDidMount") // 3
    }
}

class Son extends React.Component{
    UNSAFE_componentWillMount(){
        console.log("Son: componentWillMount") // 1
    }
    componentDidMount(){
        console.log("Son: componentDidMount") // 3
    }
    componentWillReceiveProps(){
        console.log("Son: componentWillReceiveProps")
    }
    shouldComponentUpdate(){
        console.log("Son: shouldComponentUpdate")
        // return false;
        return true;
    }
    componentWillUpdate(){
        console.log("Son: componentWillUpdate") // 
    }
    componentDidUpdate(){
        console.log("Son: componentDidUpdate") 
    }
    render(){
        console.log("Son: render")
        return(
            <div>
                <h1>子组件</h1>
                <p>{this.props.number}</p>
            </div>
        )
    }
    componentWillUnmount(){
        console.log("Son: componentWillUnmount") 
    }
}
export default Father;
复制代码

运行结果(须要下去本身慢慢分析):

图片加载失败!

React v16.4 生命周期

图片加载失败!

移除的生命周期函数:

  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

虽然废弃了这三个生命周期方法,可是为了向下兼容,将会作渐进式调整。
在16.3 版本并未删除这三个生命周期,同时还为它们新增以 UNSAFE_ 前缀为别名的三个函数 UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()
在 16.4 版本给出警告将会弃用 componentWillMount()componentWillReceiveProps()componentWillUpdate() 三个函数
而后在 17.0 版本将会删除 componentWillMount()componentWillReceiveProps()componentWillUpdate() 这三个函数,会保留使用 UNSAFE_componentWillMount()UNSAFE_componentWillReceiveProps()UNSAFE_componentWillUpdate()

新增的声明周期函数:

  • static getDerivedStateFromProps(nextProps, prevState)
  • getSnapshotBeforeUpdate(prevProps, prevState)

为何改变生命周期?

从上面的生命周期的图中能够看出,被废弃的三个函数都是在render以前,由于fiber的出现,极可能由于高优先级任务的出现而打断现有任务致使它们会被执行屡次,这确定不是你想要的结果。

另外的一个缘由则是,React想约束使用者,好的框架可以让人不得已写出容易维护和扩展的代码。

有篇文章感受写的很好新老生命周期对比

static getDerivedStateFromProps(nextProps, prevState)

在每次渲染以前都会调用,无论形成从新渲染的缘由是什么,无论初始挂载仍是后面的更新都会调用,这一点和UNSAFE_componentWillReceiveProps不一样(只有当父组件形成从新渲染时才调用)

该方法传入的两个参数:nextProps表示父组件传入的值,prevState表示组件自身的state

使用该方法,须要在该方法中返回一个对象或null:若是返回的是对象,则会更新 state,若是返回的是null,则表示不更新。

使用该方法的时候须要初始化state,不然在控制台中会出现警告信息,不能在该方法内部,调用this.state

getSnapshotBeforeUpdate(prevProps, prevState)

这个方法在 render() 以后,componentDidUpdate() 以前调用。

该方法传入的两个参数:prevProps表示更新前的 props,prevState表示更新前的 state

返回值称为一个快照(snapshot),若是不须要 snapshot,则必须显示的返回 null —— 由于返回值将做为 componentDidUpdate() 的第三个参数使用。因此这个函数必需要配合 componentDidUpdate() 一块儿使用

这个函数的做用是在真实 DOM 更新(componentDidUpdate)前,获取一些须要的信息(相似快照功能),而后做为参数传给 componentDidUpdate。例如:在getSnapShotBeforeUpdate中获取滚动位置,而后做为参数传给 componentDidUpdate,就能够直接在渲染真实的 DOM 时就滚动到须要的位置。

componentDidCatch(err,info)

在上面没说,这里说一下

任何子组件在渲染期间,生命周期方法中或者构造函数 constructor 发生错误时调用。

该方法传入的两个参数:err表示抛出的错误,info表示带有componentStack key的对象,其中包含有关组件引起错误的栈信息。

错误边界不会捕获下面的错误:

  • 事件处理 (Event handlers) (由于事件处理不发生在 React 渲染时,报错不影响渲染)
  • 异步代码 (Asynchronous code) (例如:setTimeout or requestAnimationFrame callbacks)
  • 服务端渲染 (Server side rendering)
  • 错误边界自己(而不是子组件)抛出的错误

测试代码

import React, { Component } from 'react';

class Father extends Component{
    constructor(props){
        super(props)
        this.state = {number:0} 
    }
    handleClick = ()=>{
        this.setState({number:this.state.number+1})
    }
    render(){
        return(
            <div>
                <h1>父组件</h1>
                <p>{this.state.number}</p>
                <button onClick={this.handleClick}>+</button>
                <hr/>
                <Son number={this.state.number}></Son>
            </div>
        )
    }
}
class Son extends React.Component{
    state = {
        number:0
    }
    // nextProps,prevState
    // nextProps 就是父给子传递的过来的新的数据 
    // prevState 是子的上一次的状态
    //每次更新都会触发
    static getDerivedStateFromProps(nextProps,prevState){
        // nextProps 表示父向下传递的新的值 1 2 3 4 5
        // prevState  0 0 0 
        console.log(nextProps,prevState)  // {number: 0}  {number: 0}
        let {number} = nextProps; 
        // console.log(number)
        // if(number%2 === 0){
        //     return { number:number+10 }
        // }else{
        //     return { number:number+100 }
        // }

        // prevState 表示是子组件的上一次状态
        if(number%2 === 0){
            console.log(prevState.number)
            return { number:prevState.number + number+10 }
        }else{
            console.log(prevState.number)
            return { number:prevState.number + number+100 }
        }
    }
    handleChange = ()=>{
        this.setState({
            number:this.state.number+1000
        })
    }
    render(){
        return(
            <div>
                <h1>子组件</h1>
                <p>{this.state.number}</p>
                <button onClick={this.handleChange}>改变状态</button>
            </div>
        )
    }
}

export default Father;
复制代码

运行效果:

图片加载失败!

import React, { Component } from 'react';

class News extends Component {
    constructor(props) {
        super(props)
        this.scrollRef = React.createRef();
    }
    state = {
        // news:["news6","news5","new4","news3","news2","new1"]
        news: []
    }
    componentDidMount() {
        this.timer = setInterval(() => {
            this.setState({
                news: [`${this.state.news.length}`, ...this.state.news]
            })
        }, 1000)
    }
    componentWillUnmount() {
        clearInterval(this.timer)
    }
    // 获取更新以前dom的快照
    getSnapshotBeforeUpdate() {
        // console.log(this.scrollRef.current.scrollHeight)
        // 在getSnapshotBeforeUpdate中返回了一个值,这个值会给componedDidUpdate的最后一个参数
        return this.scrollRef.current.scrollHeight;
    }
    componentDidUpdate(prevProps, prevState, lastScrollHeight) {
        console.log(lastScrollHeight)
        console.log(this.scrollRef.current.scrollTop)  // 0
        let scrollTop = this.scrollRef.current.scrollTop;
        this.scrollRef.current.scrollTop = scrollTop + (this.scrollRef.current.scrollHeight - lastScrollHeight)
    }
    render() {
        let styles = {
            height: "100px",
            width: "200px",
            border: "1px solid red",
            overflow: "auto"
        }
        return (
            <ul style={styles} ref={this.scrollRef}>
                {
                    this.state.news.map((item, index) => <li key={index}>{item}</li>)
                }
            </ul>
        )
    }
}

export default News;
复制代码

运行效果:

图片加载失败!


QAQ

相关文章
相关标签/搜索