React中类式组件的(新、旧)生命周期

目录

 生命周期(旧)

 生命周期(新)


我们在了解组件的生命周期前,我们先做如下理解:

1.组件对像从创建到死亡它会经历特定阶段。

2.Ract组件对象包含一系列勾子函数(生命周期回调函数),在特定的时刻调用。

3.我们在定义组件时,在特定的生命周期回调函数中,做特定的工作。

React中类式组件的生命周期包括旧版本的旧生命周期,也有新版本的新生命周期,本节将从新、旧两种生命周期做详细讲解。

  •  生命周期(旧)

React中旧版本的生命周期如下图所示

扫描二维码关注公众号,回复: 14733079 查看本文章

 其中主要有三个阶段:

  • 初始化阶段: 由ReactDOM.render()触发---初次渲染
  1.  constructor()   =====> 类式组件构造函数,创造类实例化对象时触发
  2. componentWillMount()  =====> 组件将要挂载(渲染)到页面上时触发
  3.  render() =====> 组件挂载(渲染)到页面上时触发
  4.  componentDidMount()  =====> 组件完成挂载(渲染)到页面上时触发  一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息
  • 更新阶段: 由组件内部this.setSate() / this.forceUpdate() 或父组件render触发;其中由父组件render触发时,子组件需要接收新的props才会触发componentWillReceiveProps(),也就是说当子组件第一次接收到props时不会触发,只有第二次才可以
  1. shouldComponentUpdate() =====>组件是否更新的钩子,当 setState()得到新的state后,根据此钩子返回的布尔值选择是否急继续更新(true:允许更新  false:禁止更新,state值也不变)
  2. componentWillUpdate() ====>组件将要更新时触发;这里需要知道 forceUpdate() 强制更新方法,无论shouldComponentUpdate() 返回什么布尔值,当我们使用forceUpdate()后,可以在不更改state值的前提下更新页面
  3. render() =====> 组件更新时重新挂载(渲染)到页面上时触发
  4. componentDidUpdate() =====> 组件完成更新的挂载后触发 
  •  卸载组件: 由ReactDOM.unmountComponentAtNode()触发
  1. componentWillUnmount()  =====> 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

我们通过如下代码进行演示(旧)生命周期:

  • 我们首先定义一个父组件Father
class Father extends React.Component{
    constructor(prop){
        console.log("Father --- constructor Father组件构造函数初始化完成")
        super(prop)
        this.state = {value:0}
    }
    //更新父组件响应事件
    updateFather = ()=>{
        let {value} = this.state
        this.setState({value:value+1})
    }
    //销毁父组件响应事件
    destroyeFather = ()=>{
        ReactDOM.unmountComponentAtNode(document.getElementById("test"))
        console.log("Father --- Father组件已经卸载")
    }
    //组件将要挂载
    componentWillMount(){
        console.log("Father --- componentWillmount Father组件将要挂载")
    }
    
    render(){
        console.log("Father --- render 渲染挂载组件")
        return (
            <div>
                <h1>我是Father组件,我已经更新了 {this.state.value} 了</h1>
                <button onClick={this.updateFather}>点击更新父组件</button>
                <button onClick={this.destroyeFather}>点击销毁父组件</button>
                <Son name="我是子组件"/>    
            </div>
        )
    }

    //组件挂载成功
    componentDidMount(){
        console.log("Father --- componentDidMount Father组件挂载成功")
    }

    //组件将要卸载
    componentWillUnmount(){
        console.log("Father --- componentWillUnmount Father组件将要卸载")
    }

    //组件是否更新
    shouldComponentUpdate(){
        console.log("Father --- shouldComponentUpdate 判断Father组件更新state后是否更新界面")
        return true
        // return false  此函数返回值为false时,界面不会更新
    }

    //组件将要更新
    componentWillUpdate(){
        console.log("Father --- componentWillUpdate Father组件将要更新")
    }

    //组件已经更新
    componentDidUpdate(){
        console.log("Father --- componentDidUpdate Father组件已经更新")
    }
}
  •  然后定义子组件Son

class Son extends React.Component{
    componentWillReceiveProps(){
        console.log("Son --- componentWillReceiveProps Father组件render时第二次接收到prop")
    }
    render(){
        return (
            <div>
                <h2>我是子组件</h2>  
            </div>
        )
    }
}
  • 最后在已有的HTML节点中渲染组件
//老版本虚拟DOM渲染节点
ReactDOM.render(<Father/>,document.getElementById("test")) 

//新版本虚拟DOM渲染节点
const root = ReactDom.createRoot(document.getElementById("test"))
root.render(<Father/>)

1.我们首先得到如下图界初始界面: 

 其中输出为:

Father --- constructor Father组件构造函数初始化完成
Father --- componentWillmount Father组件将要挂载
Father --- render 渲染挂载组件
Father --- componentDidMount Father组件挂载成功

2.当我们点击更新组件时,这里父组件Father会第二次向子组件Son传入Prop参数,此时会调用子组件Son中的componentWillReceiveProps()方法:

  其中输出为:

Father --- shouldComponentUpdate 判断Father组件更新state后是否更新界面
Father --- componentWillUpdate Father组件将要更新
Father --- render 渲染挂载组件
Son --- componentWillReceiveProps Father组件render时第二次接收到prop
Father --- componentDidUpdate Father组件已经更新

3.当我们点击销毁按钮后,组件从页面上消失,同时输出为:

Father --- componentWillUnmount Father组件将要卸载
Father --- Father组件已经卸载
  •  生命周期(新)

 我们查阅React官方文档发现新版本的React中对于组件的生命周期弃用了三个钩子:

componentWillMount()

componentWillUpdate()

componentWillReceiveProps()

因为React团队一直致力于异步渲染,而上述三个生命周期的代码在 React 的未来版本中更有可能出现 bug,尤其是在启用异步渲染之后,所以在后续版本中要使用上述三个生命周期需要在钩子前边加上“UNSAFE_” 前缀。因此新的生命周期实现如图:

新的生命周期中新增了两个钩子:getDerivedStateFromProps()  getSnapshotBeforeUpdate()  

  • getDerivedStateFromProps()

 此钩子不由实例调用,因此声明方式为:

static getDerivedStateFromProps(props,state)

getDerivedStateFromProps接收两个参数,且会在调用render方法之前调用,并且在初始挂载及后续更新时都会被调用。它应返回一个state对象或者null:如果返回null则不影响后续渲染更新;如果返回state对象,则后续对于state的修改就会失效,state值会一直保持不变,即这里如果返回参数中的组件三大属性之一的props,就会将props当作派生state使用,且派生state的值不会由于setState()方法更新,即state的值在任何时候都取决于props。例如:

class MyComponent extends React.Component{
    state = {nums : 0}
    
    //......
    //此处省略其他钩子
    //......
    static getDerivedStateFromProps(props,state)
    {
        //这里返回的类似于state的对象
        //如果其中包含的属性与真实state一样,即nums和nums,则通过setState()对nums的更新再也无效
        return {nums:999} 

        //返回的来自props的派生state
        //如果其中包含的属性与真实state一样,即nums和nums,则通过setState()对nums的更新再也无效
        return props 

        //返回null对state的更新无影响
        return null
    }
    
    //......
    //此处省略其他钩子
    //......

}

//这里传入的props在getDerivedStateFromProps返回后会得到来自props的派生state
 ReactDOM.render(<MyComponent/ nums={999}>,document.getElementById("test"))

派生状态会导致代码的冗余,增加组件的复杂性,故不要轻易使用该钩子。

  • getSnapshotBeforeUpdate()  
getSnapshotBeforeUpdate(preProps,preState)

此钩子接收两个参数:preProps(React更新前上一次的Props)、preState(React更新前上一次的State)。

getSnapshotBeforeUpdate() 在最近一次渲染输出(提交到DOM节点)之前,即更新完成挂载前调用。它使得组件能在发生更改之前从DOM中捕获一些信息(例如,滚动位置)。此生命周期的任何返回值将作为参数snapshotValue传给componentDidUpdate(preProps,preState,snapshotValue)。

该钩子的使用案例如下所示:

假设我们在如下所示改变滚动条位置时,新添加的标签会将我们滚动查看的标签不断挤下去

 而我们要达到如下效果

 此时我们就要使用getSnapshotBeforeUpdate(),在更新成功前,我们保存未更新前的scrollHeight,并将其返回到componentDidUpdate()中,从而得到因为元素更新挤下去的相对位移。代码实现如下:

  <style>
        .news{
            background-color:antiquewhite;
            overflow: auto;
            width: 150px;
            height: 140px;
        }
        .val{
            height: 20px;
            color: black;
        }
    </style>


class Scroll extends React.Component{
    state={arrayNews:[]} //初始化新闻条目

    componentDidMount(){
        //不断添加标签到页面上
        setInterval(()=>{
            const {arrayNews} = this.state
            const addNews = `我是第${arrayNews.length+1}条新闻`
            this.setState({arrayNews:[addNews,...arrayNews]})
            
        },1000)
    }



    render(){
        return(
            <div className="news" ref={(c) => this.news = c}>
                {this.state.arrayNews.map((val,idx)=>
                    { 
                    return (
                    <div className="val" key={idx}>{val}</div>)
                    }
                    )
                }
            </div>
        )
    }

    //在更新成功前,我们保存未更新前的scrollHeight,并将其返回到componentDidUpdate()中
    getSnapshotBeforeUpdate()
    {
        return this.news.scrollHeight
    }

    //更新成功后,我们将当前的scrollHeight减去getSnapshotBeforeUpdate()返回的scrollHeight,即得到因为元素更新挤下去的相对位移
    componentDidUpdate(preProps,preState,snapshot)
    {
        //更新当前scrollTop,保持当前可是页面不会因为元素增加而被滑走
        this.news.scrollTop += this.news.scrollHeight - snapshot
    }
}

//老版本虚拟DOM渲染节点
ReactDOM.render(<Scrool/>,document.getElementById("test")) 

//新版本虚拟DOM渲染节点
const root = ReactDom.createRoot(document.getElementById("test"))
root.render(<Scrool/>)

我们对新的生命周期进行总结:

  • 初始化阶段: 由ReactDOM.render()触发---初次渲染

1.  constructor()  =====> 类式组件构造函数,创造类实例化对象时触发

2.  getDerivedStateFromProps() =====> 返回null或者获得派生state,这里如果返回state对象,后续state对象将不能被更改

3.  render() =====> 组件挂载(渲染)到页面上时触发

4.  componentDidMount() =====> 一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  • 更新阶段: 由组件内部this.setSate()或父组件重新render触发

1.  getDerivedStateFromProps () =====> 返回null或者获得派生state,这里如果返回state对象,后续state对象将不能被更改

2.  shouldComponentUpdate() =====>组件是否更新的钩子,当 setState()得到新的state后,根据此钩子返回的布尔值选择是否急继续更新(true:允许更新  false:禁止更新,state值也不变)

3.  render() =====> 组件更新时重新挂载(渲染)到页面上时触发

 4.  getSnapshotBeforeUpdate() =====> 在组件更新完成前,得到一些重要的值返回给componentDidUpdate() 

5.  componentDidUpdate() ====> 组件完成更新的挂载后触发 

  • 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

componentWillUnmount()  =====> 一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

猜你喜欢

转载自blog.csdn.net/weixin_44384728/article/details/128376213