国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【學(xué)習(xí)前端第七十四課】React生命周期

這篇具有很好參考價(jià)值的文章主要介紹了【學(xué)習(xí)前端第七十四課】React生命周期。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

React生命周期

我們這里所說(shuō)的生命周期是基于類式組件的,其中主要分為三個(gè)階段

  1. 掛載
  2. 更新
  3. 卸載

以上每一個(gè)階段內(nèi)都有自己的一系列在當(dāng)前生命周期中可被自動(dòng)執(zhí)行的生命周期函數(shù)(類似于vue的鉤子函數(shù))

掛載階段(初始化)

在掛載階段按照?qǐng)?zhí)行順序分別有以下幾個(gè)生命周期函數(shù)

  1. constructor() 初始化整個(gè)組件的state 以及調(diào)用super(props),該方法只會(huì)執(zhí)行一次
  2. componentWillMount() 掛載之前(已經(jīng)棄用,目前React18.2中依然還是可以使用,如果使用會(huì)報(bào)警告)
  3. render() 只返回需要渲染頁(yè)面結(jié)構(gòu),不要包含其它的業(yè)務(wù)邏輯
  4. componentDidMount() 掛載之后,可以獲取到DOM節(jié)點(diǎn)并操作,服務(wù)器請(qǐng)求,啟用事件監(jiān)聽(tīng),調(diào)用 setState()等都可以在此函數(shù)中完成,該方法只會(huì)執(zhí)行一次

在組件代碼中表示如下

import React, { Component } from 'react'

export default class Mount extends Component {
    //掛載階段第一個(gè)執(zhí)行的生命周期函數(shù)
    constructor(props){
        super(props)
        console.log("1、初始化")
    }
    //掛載階段第二個(gè)執(zhí)行的生命周期函數(shù)
    componentWillMount(){
        console.log("2、掛載之前")
    }
    //掛載階段第三個(gè)執(zhí)行的生命周期函數(shù)
    render() {
        console.log("3、渲染頁(yè)面結(jié)構(gòu)")
        return (
        <div>
            
        </div>
        )
    }
    //掛載階段第四個(gè)執(zhí)行的生命周期函數(shù)
    componentDidMount(){
        console.log("4、掛載之后")
    }
}
注意事項(xiàng):

在執(zhí)行過(guò)程中我們會(huì)發(fā)現(xiàn)如下情況

1、控制臺(tái)會(huì)報(bào)一個(gè)警告,這個(gè)警告主要是告訴我們關(guān)于componentWillMount函數(shù)的名稱在React18.x的版本中名字要做修改,如下

UNSAFE_componentWillMount(){
 console.log("2、掛載之前")
}

2、按照上面的方式修改之后又會(huì)又一個(gè)警告,告訴我們?cè)趪?yán)格模式下使用 UNSAFE_ 的寫法可以會(huì)導(dǎo)致提示代碼中存在錯(cuò)誤,這個(gè)可以把在入口文件index.js中設(shè)置的React.StrictMode刪除掉,就不會(huì)有警告了

3、如果在嚴(yán)格模式下,我們會(huì)發(fā)現(xiàn)我們的生命周期函數(shù)出了componentWillMount之外的其他三個(gè)全部都執(zhí)行了兩邊,把嚴(yán)格模式刪除掉即可

擴(kuò)展內(nèi)容:為什么會(huì)渲染兩次?

之所以這些生命周期函數(shù)會(huì)被渲染兩次的原因在于React.StrictMode嚴(yán)格模式所提供的好處之一,它幫助我們檢測(cè)生命周期函數(shù)在執(zhí)行渲染時(shí)的副作用,而嚴(yán)格模式下的檢測(cè)方式就是故意調(diào)用一些關(guān)鍵函數(shù)兩次,來(lái)幫助發(fā)現(xiàn)副作用

這種渲染兩次的行為會(huì)對(duì)性能造成一定影響,但是它只針對(duì)開發(fā)環(huán)境,在生產(chǎn)環(huán)境中不會(huì)發(fā)生渲染兩次的情況

更新階段

更新階段分為兩種情況,分別是props更新時(shí)和state更新時(shí),我們先來(lái)看state更新時(shí)會(huì)執(zhí)行的生命周期函數(shù)

state更新時(shí)按照?qǐng)?zhí)行順序會(huì)觸發(fā)如下函數(shù):
  1. shouldComponentUpdate() 根據(jù)return的布爾值來(lái)決定是否開始更新數(shù)據(jù)
  2. componentWillUpdate() 準(zhǔn)備開始更新
  3. render() 重新渲染頁(yè)面結(jié)構(gòu)
  4. componentDidUpdate() 更新之后

注意:

  1. componentWillReceiveProps()方法會(huì)把新更新props接收為參數(shù)
  2. 如果shouldComponentUpdate() 方法return的是false,則后面三個(gè)函數(shù)不會(huì)執(zhí)行,同時(shí),這個(gè)方法可以接收兩個(gè)參數(shù),分別是更新之后的state數(shù)據(jù)和更新之后的props數(shù)據(jù)

代碼演示:

import React, { Component } from 'react'

class Son extends Component {
    state = {
        str:"hoho"
    }
    componentWillReceiveProps(newProps){
        console.log("0、當(dāng)接收到新的props",newProps)
    }
    shouldComponentUpdate(newProps,newState){
        console.log("1、是否更新數(shù)據(jù)",newProps,newState);
        return true
    }
    componentWillUpdate(){
        console.log("2、準(zhǔn)備開始更新")
    }
    render(){
        console.log("3、重新渲染頁(yè)面結(jié)構(gòu)")
        return (
            <div>
                <button onClick={this.changeStr.bind(this)}>更新state</button>
            </div>
        )
    }
    componentDidUpdate(){
        console.log("4、更新之后")
    }
	//制作一個(gè)修改state的方法觸發(fā)生命周期的更新階段
    changeStr(){
        this.setState({
            str:"haha"
        })
    }
}

export default class Mount extends Component {
    state = {
        num:1
    }
	//修改父組件傳入子組件的num觸發(fā)componentWillReceiveProps
    changeNum(){
        this.setState({
            num:1
        })
    }
    render(){
        return (
            <div>
                <button onClick={this.changeNum.bind(this)}>修改num</button>
                <Son num={this.state.num}></Son>
            </div>
        )
    }
}
注意事項(xiàng):
  • componentWillReceiveProps()函數(shù)會(huì)報(bào)警告要替換成新的 static getDerivedStateFromProps() 或者加上UNSAFE_前綴
  • componentWillUpdate() 函數(shù)也會(huì)遇到上面掛載階段函數(shù)名修改的警告提示,把該函數(shù)的名稱也加上UNSAFE_ 前綴即可
UNSAFE_componentWillUpdate(){
 console.log("2、準(zhǔn)備開始更新")
}
數(shù)據(jù)修改過(guò)程做性能優(yōu)化

在上面的 shouldComponentUpdate() 函數(shù)中,我們知道它會(huì)被自動(dòng)注入兩個(gè)參數(shù),分別是修改后的props與修改后的state,而是否真的去做數(shù)據(jù)更新會(huì)根據(jù)這個(gè)函數(shù)的return值來(lái)決定,這里我們就可以手寫一個(gè)業(yè)務(wù)邏輯來(lái)對(duì)程序進(jìn)行性能優(yōu)化

業(yè)務(wù)邏輯如下:

如果修改之后的state值與修改之前的state值是一樣的,那我們就沒(méi)有必要專門再把數(shù)據(jù)更新一遍,因?yàn)檫@樣做會(huì)重新執(zhí)行一遍render渲染頁(yè)面結(jié)構(gòu)

把上面的業(yè)務(wù)邏輯轉(zhuǎn)換成代碼表示

shouldComponentUpdate(newProps,newState){
    //newState接收的是一個(gè)state對(duì)象,修改的新值在對(duì)象的屬性中
    if(newState.str === this.state.str){
        return false
    }else{
        return true
    }
}

上面的代碼過(guò)于冗余,做如下修改

shouldComponentUpdate(newProps,newState){
    return newState.str !== this.state.str
}
props更新時(shí)會(huì)觸發(fā)以下生命周期函數(shù)執(zhí)行

props更新時(shí),所執(zhí)行的生命周期函數(shù)與執(zhí)行順序和state更新時(shí)一樣,只不過(guò)會(huì)在最前面添加一個(gè)生命周期函數(shù)的執(zhí)行

  1. componentWillReceiveProps() 外部傳入的數(shù)據(jù)發(fā)生變化時(shí)觸發(fā)(父組件修改了一個(gè)自己組件內(nèi)部的數(shù)據(jù),而這個(gè)被修改的數(shù)據(jù)時(shí)有傳遞給子組件的)
  2. shouldComponentUpdate() 根據(jù)return的布爾值來(lái)決定是否接收外部傳入的新值
  3. componentWillUpdate() 準(zhǔn)備開始更新
  4. render() 重新渲染頁(yè)面結(jié)構(gòu)
  5. componentDidUpdate() 更新之后

**注意:**與上面的state更新時(shí)一樣,當(dāng)外部傳入的數(shù)據(jù)改變的時(shí)候,也會(huì)根據(jù) shouldComponentUpdate 返回的布爾值來(lái)決定是否要接收在外部已經(jīng)被修改的新數(shù)據(jù),如果return false則后面三個(gè)函數(shù)不執(zhí)行

創(chuàng)建組件進(jìn)行代碼演示:

import React, { Component } from 'react'

class Child extends Component {
    UNSAFE_componentWillReceiveProps(){
        console.log("0、外部傳入的props被修改")
    }
    shouldComponentUpdate(newProps,newState){
        console.log("1、是否要更新數(shù)據(jù)")
        return true
    }
    UNSAFE_componentWillUpdate(){
        console.log("2、準(zhǔn)備開始更新")
    }
    render(){
        console.log("3、重新渲染頁(yè)面結(jié)構(gòu)")
        return (
            <div>
                <h1>{this.props.cstr}</h1>
            </div>
        )
    }
    componentDidUpdate(){
        console.log("4、更新之后")
    }
}

//制作一個(gè)父組件
export default class Father extends Component {
    state = {
        fstr:"father"
    }
    changeStr(){
        this.setState({
            fstr:"haha"
        })
    }
    render() {
        return (
        <div>
            <button onClick={this.changeStr.bind(this)}>修改父組件的fstr</button>
            <Child cstr={this.state.fstr}></Child>
        </div>
        )
    }
}
把props的修改也考慮到數(shù)據(jù)修改的性能優(yōu)化上

如果props修改之后的值與修改之前的一樣,那么就不執(zhí)行更新

shouldComponentUpdate(newProps,newState){
    return (newState.str !== this.state.str) || (newProps.cstr !== this.props.cstr)
}
PureComponent

PureComponent是React15.3版本新添加的一個(gè)繼承自Component的子類,當(dāng)我們的類組件繼承自PureComponent的時(shí)候,該組件會(huì)自動(dòng)加載shouldComponentUpdate聲明周期函數(shù),當(dāng)組件更新的時(shí)候會(huì)自動(dòng)對(duì)組件的props和state進(jìn)行新舊比較,如果沒(méi)有發(fā)生變化就不會(huì)觸發(fā)render方法讓組件二次渲染,就從可以達(dá)到和上面我們手動(dòng)編寫的優(yōu)化方法一樣的效果

import React, { Component,PureComponent } from 'react'

export default class Mount extends PureComponent {
   	//當(dāng)類組件繼承自PureComponent就相當(dāng)于自動(dòng)實(shí)現(xiàn)了,如下代碼
    /*
    shouldComponentUpdate(newProps,newState){
        return (newState.str !== this.state.str) || (newProps.cstr !== this.props.cstr)
    }
    */
}

卸載階段

卸載可以理解成到組件切換的時(shí)候,切換之前的組件會(huì)被卸載掉,在這個(gè)階段只有一個(gè)生命周期函數(shù)

1、componentWillUnmount() 當(dāng)組件卸載時(shí)觸發(fā)

創(chuàng)建組件進(jìn)行代碼演示:

import React, { Component } from 'react'

export default class Unmount extends Component {
  render() {
    return (
      <div>
        	
      </div>
    )
  }
  componentWillUnmount(){
        console.log("當(dāng)組件卸載時(shí)")
  }
}

然后,我們?cè)趇ndex.js中通過(guò)setTimeout制作對(duì)root中渲染的組件做一個(gè)延遲執(zhí)行

//......
import Unmount from './components/Unmount';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <Unmount></Unmount>
);
//3秒之后渲染App組件,這個(gè)時(shí)候Unmount組件就會(huì)被替換掉從而觸發(fā)卸載生命周期函數(shù)
setTimeout(() => {
    root.render(
        <App></App>
    )
},3000)

我們來(lái)通過(guò)一個(gè)具體的小例子來(lái)看待卸載生命周期函數(shù)的作用

上面的index.js中的內(nèi)容不動(dòng),依然模擬一個(gè)組件卸載的過(guò)程,然后把Unmount組件做一些修改

import React, { Component } from 'react'

export default class Unmount extends Component {
    //到組件掛載之后給整個(gè)網(wǎng)頁(yè)文檔綁定一個(gè)點(diǎn)擊事件
    componentDidMount(){
        document.addEventListener("click",this.dbClick)
    }
    //制作一個(gè)觸發(fā)事件的方法
    dbClick(){
        console.log("我點(diǎn)擊了這個(gè)網(wǎng)頁(yè)")
    }
    render() {
        return (
        <div>
            
        </div>
        )
    }
    //到組件卸載的時(shí)候,把綁定在整個(gè)文檔上的事件移除掉
    componentWillUnmount(){
            console.log("當(dāng)組件卸載時(shí)")
            document.removeEventListener("click",this.dbClick)
    }
}

代碼分析:

上面的代碼主要情況就是,我們?cè)赨nmount組件掛載之后給全局綁定了一個(gè)點(diǎn)擊事件,所以就算你組件卸載了,這個(gè)事件也不會(huì)受影響,所以我們?cè)谛遁d的時(shí)候再次移除這個(gè)全局事件,從而保證雖然是一個(gè)全局事件,但是這個(gè)事件可以跟隨Unmount組件的生命周期來(lái)走

React16+新增生命周期函數(shù)

在上面介紹的生命周期函數(shù)中,有部分是在新版本中被棄用的,取而代之的也有一些新增的生命周期函數(shù)

getDerivedStateFromProps函數(shù)

這是一個(gè)靜態(tài)方法,所以在類組件中聲明時(shí)記得帶上static修飾符,它主要替代了掛載階段中componentWillMount和更新階段中componentWillReceiveProps這兩個(gè)函數(shù)的位置,也就是說(shuō) getDerivedStateFromProps 在在初始化和后續(xù)更新都會(huì)被調(diào)用

該方法會(huì)接收兩個(gè)參數(shù)

參數(shù)1:nextProps即將更新的props,父組件更新了傳入到子組件中的數(shù)據(jù)時(shí),更新后的數(shù)據(jù)會(huì)傳入此參數(shù)

參數(shù)2:prevState 子組件內(nèi)部的更新之前的狀態(tài)(舊的狀態(tài))

**返回值:**該方法會(huì)返回一個(gè)對(duì)象來(lái)更新組件的內(nèi)部狀態(tài)state,如果返回null則不更新

注意事項(xiàng):

使用該方法時(shí),一定要設(shè)置state,就算是個(gè)空對(duì)象都行

在介紹該方法的作用之前我們需要先介紹擴(kuò)展介紹一些知識(shí)點(diǎn)

派生狀態(tài)

在介紹 getDerivedStateFromProps 方法的使用之前我們要先理解一種特殊的組件內(nèi)部狀態(tài),派生狀態(tài),而介紹派生狀態(tài)之前我們還要簡(jiǎn)單復(fù)習(xí)并擴(kuò)展一下受控與非受控組件的內(nèi)容

之前我們有介紹過(guò)受控組件和非受控組件,“受控”和“不受控制”主要在我們之前的介紹中主要指的是表單數(shù)據(jù),但它還可以用于描述組件與組件之間的控制關(guān)系。作為props傳遞進(jìn)子組件的數(shù)據(jù)可以被認(rèn)為是受控的,所以我們認(rèn)為該子組件的數(shù)據(jù)受控與父組件。只存在于內(nèi)部狀態(tài)state的數(shù)據(jù)可以被認(rèn)為是不受控制的,因?yàn)檫@個(gè)數(shù)據(jù)不受父組件的控制。

舉例

import React, { Component } from 'react'

export default class Mount extends Component { //父組件
    constructor(props){
        super(props)
        this.state={
            textVal:"zhangsan"
        }
    }
    changeVal(){
        this.setState({
            textVal:"lisi"
        })
    }
    render(){
        return (
            <div>
                <h2>父組件:{this.state.textVal}</h2>
                <button onClick={this.changeVal.bind(this)}>修改父組件state按鈕</button>
                <Son textVal={this.state.textVal} changeVal={this.changeVal.bind(this)}></Son>               
            </div>
        )
    }
}

class Son extends Component {   //子組件
    constructor(props){
        super(props)
        this.state = {
            sonVal:this.props.textVal
        }
    }
    changeVal(){
        this.setState({
            sonVal:"lisi"
        })
    }
    render(){
        return (
            <div>
                <h2>子組件state調(diào)用:{this.state.sonVal}</h2>
                <h2>子組件props調(diào)用:{this.props.textVal}</h2>    
                <button onClick={this.changeVal.bind(this)}>修改子組件state按鈕</button>
            </div>
        )
    }
}

代碼分析:

以上的情況我們可以認(rèn)為,父組件向子組件傳入了數(shù)據(jù)“zhangsan” ,但是在子組件中我們通過(guò) sonVal:this.props.textVal 將該數(shù)據(jù)指派給了子組件自己的內(nèi)部狀態(tài)sonVal

然后我們?cè)賮?lái)通過(guò)父組件Mount中修改狀態(tài)的方法changeVal修改父組件的狀態(tài)值時(shí),子組件中sonVal的值并不會(huì)跟著一起改變,這種情況下雖然子組件的sonVal的值是通過(guò)父組件傳入賦值得到的,但是它并不受控與父組件

派生狀態(tài)的使用就是為了讓上面這種情況下的子組件中的sonVal依然還可以受控與父組件,也就是說(shuō)子組件的內(nèi)部狀態(tài)值會(huì)根據(jù)父組件傳入數(shù)據(jù)的改變而改變

注意點(diǎn):

派生狀態(tài)實(shí)現(xiàn)的功能與直接在子組件中通過(guò)props調(diào)用渲染父組件傳入的數(shù)據(jù)所形成的結(jié)果十分相似,但是其原理是不同的

  • props的實(shí)現(xiàn)方式始終都沒(méi)有觸及到子組件自身的內(nèi)部狀態(tài),一致都使用的是父組件的數(shù)據(jù)
  • 派生狀態(tài)的實(shí)現(xiàn)其本質(zhì)還是調(diào)用的組件自身的內(nèi)部狀態(tài),子組件通過(guò)props將父組件傳入的數(shù)據(jù)賦值給子組件的狀態(tài)

如何實(shí)現(xiàn)派生狀態(tài)

getDerivedStateFromProps這個(gè)方法的目的就只有一個(gè),就是實(shí)現(xiàn)派生狀態(tài),使組件可以根據(jù)props的結(jié)果來(lái)更新其內(nèi)部狀態(tài)

注意:謹(jǐn)慎使用派生狀態(tài)

舉例:

import React, { Component } from 'react'

export default class Father extends Component {
    state = {
        fatherVal: "hello"
    };
    ChangeVal(e){
        this.setState({ fatherVal: e.target.value });
    };
    render(){
        return (   
            <div>
                父組件:<input type="text" value={this.state.fatherVal} onChange={this.ChangeVal.bind(this)} />
                <Son textVal={this.state.fatherVal}></Son>
            </div>
        )
    }
}

class Son extends Component {
    state = {
        sonVal:this.props.textVal
    }
    render() {
        return <h2>子組件:{this.state.sonVal}</h2>;
    }
    static getDerivedStateFromProps(nextProps){
        return {sonVal: nextProps.textVal}
    }       
    // componentWillReceiveProps(nextProps) {
    //     this.setState({ sonVal: nextProps.textVal });
    // }
}

代碼分析:

這里我們實(shí)現(xiàn)了派生狀態(tài),將father組件中的狀態(tài)fatherVal傳入Son組件,并指派給Son組件的內(nèi)部狀態(tài)sonVal,當(dāng)父組件觸發(fā)changeVal使父組件狀態(tài)發(fā)生改變觸發(fā)了組件更新進(jìn)行重新渲染,而這個(gè)渲染過(guò)程也會(huì)波及到子組件使得子組件也會(huì)一并更新,而子組件的更新也必然會(huì)觸發(fā)其生命周期函數(shù) getDerivedStateFromProps 將父組件更新后的傳入子組件中的數(shù)據(jù)從props中調(diào)出再次賦值給子組件的內(nèi)部狀態(tài)

使用派生狀態(tài)遇到的常見(jiàn)bug

舉例:對(duì)上面的例子做一點(diǎn)修改,現(xiàn)在我們把子組件中的派生狀態(tài)sonVal渲染到一個(gè)表單中然后想讓該表單與sonVal實(shí)現(xiàn)雙向綁定

import React, { Component } from 'react'

export default class Father extends Component {  //父組件內(nèi)部不變
    state = {
        fatherVal: "hello",
        num:1
    };
    changeVal(e){
        this.setState({ fatherVal: e.target.value });
    };
    render(){
        return (   
            <div>
                父組件:<input type="text" value={this.state.fatherVal} onChange={this.changeVal.bind(this)} />
                <hr />
                <Son textVal={this.state.fatherVal}></Son>
            </div>
        )
    }
}

class Son extends Component {
    state = {
        sonVal:this.props.textVal,
    }
	//制作雙向綁定的方法
    changeInput(e){
        this.setState({sonVal:e.target.value})
    }
    render() {
        return (
            //把sonVal渲染把一個(gè)單表上,然后綁定onChange事件執(zhí)行雙向綁定
            <div>
                <h2>子組件:{this.state.sonVal}</h2>
                <input type="text" value={this.state.sonVal} onChange={this.changeInput.bind(this)} />
            </div>
        )        
    }
    static getDerivedStateFromProps(nextProps){
        return {sonVal: nextProps.textVal}
    }       
    // componentWillReceiveProps(nextProps) {
    //     this.setState({ sonVal: nextProps.textVal });
    // }
}

代碼分析:

上面這樣寫是有問(wèn)題的,我們會(huì)發(fā)現(xiàn)這個(gè)input里面永遠(yuǎn)無(wú)法輸入新的值進(jìn)去

原因:

其實(shí)并不是寫不進(jìn)去數(shù)據(jù),而是每當(dāng)我們輸入一個(gè)數(shù)據(jù)的時(shí)候就會(huì)觸發(fā)組件的更新,而組件一更新就會(huì)重新渲染,而一旦重新渲染sonVal的值就又重新被指派成了父組件傳入的textVal的值,在input的value上面渲染的數(shù)據(jù)就又會(huì)變成父組件指派的數(shù)據(jù),周而復(fù)始,只要一直持續(xù)輸入新數(shù)據(jù)就一直這樣,導(dǎo)致在input中新輸入的數(shù)據(jù)一直被覆蓋掉,無(wú)法將新數(shù)據(jù)保留下來(lái)

解決方案:完全受控組件

直接全程props,從子組件中徹底刪除狀態(tài),子組件直接使用props不再去組件內(nèi)部接收父組件的指派狀態(tài)

import React, { Component } from 'react'

export default class Father extends Component {
state = {
  fatherVal: "hello",
  num:1
};
changeVal(e){
  this.setState({ fatherVal: e.target.value });
};
render(){
  return (   
      <div>
          父組件:<input type="text" value={this.state.fatherVal} onChange={this.changeVal.bind(this)} />
          <hr />
          <Son textVal={this.state.fatherVal} changeVal={this.changeVal.bind(this)}></Son>
      </div>
  )
}
}

class Son extends Component {
state = {
  sonVal:this.props.textVal,
}
render() {
  return (
      <div>
          <h2>子組件:{this.state.sonVal}</h2>
          <input type="text" value={this.props.textVal} onChange={this.props.changeVal.bind(this)} />
      </div>
  )        
}
}

getSnapshotBeforeUpdate函數(shù)

這是一個(gè)新增的更新階段的生命周期函數(shù),其在render后觸發(fā),其接收兩個(gè)參數(shù),還有一個(gè)返回值

參數(shù)1:更新之前的props

參數(shù)2:更新之前的state

返回值:該返回值會(huì)作為componentDidUpdate生命周期函數(shù)的第三個(gè)參數(shù)傳入,一般我們會(huì)返回更新之前的一些頁(yè)面狀態(tài),從而進(jìn)行頁(yè)面的狀態(tài)保持

注意:新舊生命周期函數(shù)不能同時(shí)使用,不然會(huì)報(bào)錯(cuò)

舉例:

現(xiàn)在有一個(gè)移動(dòng)端頁(yè)面,我現(xiàn)在希望當(dāng)組件更新之后,調(diào)整當(dāng)前滾動(dòng)的位置

import React, { Component } from 'react'
import './snapshot.css'

export default class SnapShot extends Component {
    state = {
        num:1
    }
	//當(dāng)組件加載時(shí)設(shè)置一個(gè)定時(shí)器,4秒之后修改狀態(tài)從而能夠觸發(fā)組件更新
    componentDidMount(){
        this.clearTime = setTimeout(() => {
            this.setState({
                num:2
            })
        },4000)
        console.log(this.scrollSize)
    }
	//一般情況下,我們都會(huì)把組件內(nèi)設(shè)置的定時(shí)器,在組件卸載時(shí)清除掉
    componentWillUnmount(){
        clearTimeout(this.clearTime)
    }
    render() {
        return (
        //通過(guò)ref獲取到滾動(dòng)容器的DOM
        <div className='wrap' ref={ref => this.wrap = ref}>
                <div className="box1">1111</div>
                <div className="box2">{this.state.num}</div>
        </div>
        )
    }
	//當(dāng)開始更新時(shí),將更新之前的wrap元素的scrollTop返回出去
    getSnapshotBeforeUpdate(prevProps,prevState){
        return this.wrap.scrollTop
    }
	//在更新之后,通過(guò)componentDidUpdate的第三個(gè)參數(shù)調(diào)出wrap元素更新之前的scrollTop的值減去300,再賦值給更新之后的wrap
    componentDidUpdate(prevProps,prevState,prevScrollTop){
        this.wrap.scrollTop = prevScrollTop - 300
    }
}

SnapShot.css

*{
    padding:0;
    margin:0;
}
.wrap{
    width:100vw;
    height:100vh;
    overflow: auto;
}
.box1{
    height:1000px;
    background:#f00;
}
.box2{
    height:1000px;
    background:#0f0;
}

代碼分析:

上面的例子中,我們的SnapShot組件應(yīng)該會(huì)在加載好4秒之后自動(dòng)修改內(nèi)部狀態(tài),從而觸發(fā)更新周期,然后在當(dāng)前滾動(dòng)的位置向上自動(dòng)滾動(dòng) 300px 的位置,這里主要依靠的就是 getSnapshotBeforeUpdate 將更新之前的scrollTop的值返回出去,然后更新之后的生命周期函數(shù) componentDidUpdate 可以調(diào)用到,并將更新之前的滾動(dòng)位置信息減去300再賦值給更新之后的scrollTop

特殊生命周期函數(shù) – React異常捕獲

從React16開始引入了一個(gè)新的概念叫做錯(cuò)誤邊界

錯(cuò)誤邊界可以理解成是一個(gè)React組件,其應(yīng)用場(chǎng)景與概念比較類似于我們之前在vue中講過(guò)的異步組件,只不過(guò)它并不是一個(gè)Promise對(duì)象,這種組件可以捕獲并打印發(fā)生在其子組件樹任何位置的原生JavaScript錯(cuò)誤,并且渲染出錯(cuò)誤情況下的UI界面,而不是渲染錯(cuò)誤的子組件樹

目前只有類組件可以成為錯(cuò)誤邊界組件,要實(shí)現(xiàn)錯(cuò)誤邊界組件需要使用以下兩個(gè)函數(shù)

  • getDerivedStateFromError(error)
  • componentDidCatch(error,info)

以上兩個(gè)函數(shù)任意一個(gè)或者兩個(gè)都用都可以構(gòu)成一個(gè)錯(cuò)誤邊界組件,兩個(gè)函數(shù)的功能類似

getDerivedStateFromError(error)

該方法是一個(gè)靜態(tài)方法,接收一個(gè)參數(shù),并返回一個(gè)對(duì)象

參數(shù):錯(cuò)誤信息

返回值:返回一個(gè)匿名對(duì)象,這個(gè)對(duì)象直接指向當(dāng)前組件的state

舉例:

import React, { Component } from 'react'

export default class ErrorComp extends Component {
    constructor(props){
        super(props)
        this.state = {
            error:false  //error用來(lái)表示當(dāng)前錯(cuò)誤邊界組件是否有捕獲到錯(cuò)誤
        }
    }
    static getDerivedStateFromError(error){
        console.log(error) //打印錯(cuò)誤
        return {
            error:true  //這里error是ErrorCpmp組件的內(nèi)部狀態(tài)error
        }
    }
    render() {
        if(this.state.error){
            return (               
                    <p>我是錯(cuò)誤位置:{this.state.text}</p>
            )
        }else{
            return (
                    <Child></Child>
            )            
        }        
    }
}

class Child extends Component {
    render(){
        throw new Error("我是一個(gè)錯(cuò)誤")  //在子組件中認(rèn)為拋出一個(gè)錯(cuò)誤來(lái)觸發(fā)getDerivedStateFromError
        return (
            <div>渲染Child組件</div>
        )
    }
}

代碼分析:

上面的代碼中,我們創(chuàng)建了一個(gè)ErrorComp組件作為錯(cuò)誤邊界組件,在內(nèi)部調(diào)用 getDerivedStateFromError ,同時(shí)創(chuàng)建一個(gè)子組件Child并在其渲染的時(shí)候手動(dòng)拋出一個(gè)錯(cuò)誤,當(dāng)組件渲染時(shí),ErrorComp接收到子組件Child拋出的錯(cuò)誤觸發(fā) getDerivedStateFromError 通過(guò)該方法的返回值修改error狀態(tài)的值為true,在ErrorComp組件render時(shí),根據(jù)error狀態(tài)的值做一個(gè)判斷來(lái)決定渲染哪個(gè)子組件

componentDidCatch(error,info)

該方法的作用其實(shí)與上面的方法基本上一致,就是執(zhí)行邏輯不太一樣,接收兩個(gè)參數(shù),沒(méi)有返回值

參數(shù)1:錯(cuò)誤信息

參數(shù)2:自動(dòng)注入一個(gè)對(duì)象,對(duì)象內(nèi)部有一個(gè)componentStack屬性記錄當(dāng)前錯(cuò)誤的路徑

舉例:

import React, { Component } from 'react'

export default class ErrorComp extends Component {
    constructor(props){
        super(props)
        this.state = {
            error:false,
            text:''
        }
    }
    componentDidCatch(error,info){
        console.log(error,info)
        this.setState({  //通過(guò)setState修改內(nèi)部狀態(tài)(與上面的return其實(shí)是一樣的)
            error:error,
            text:info.componentStack //把錯(cuò)誤信息路徑賦值給text
        })
    }
    render() {
        return (
            <div>
                {
                    //即然是判斷,我們也可以采用三元運(yùn)算完成
                    this.state.error ? <p>{this.state.text}</p> : <Child></Child>
                }
            </div>            
        )      
    }
}

class Child extends Component {
    render(){
        throw new Error("我是一個(gè)錯(cuò)誤")
        return (
            <div>渲染Child組件</div>
        )
    }
}

代碼分析:

上面的例子其實(shí)執(zhí)行的效果與getDerivedStateFromError方法的執(zhí)行效果一樣,只不過(guò)可以多接收一個(gè)參數(shù)記錄錯(cuò)誤位置,一般我們可以把這個(gè)錯(cuò)誤信息作為錯(cuò)誤日志上傳給服務(wù)器,同時(shí)因?yàn)闆](méi)有return的方式修改錯(cuò)誤狀態(tài)error的值,所以我們直接使用setState來(lái)修改

以上兩個(gè)方法如果要一起用的話,我們可以用 getDerivedStateFromError 來(lái)修改錯(cuò)誤狀態(tài)值,用componentDidCatch 來(lái)調(diào)用一個(gè)上傳接口上傳把錯(cuò)誤信息作為上傳錯(cuò)誤日志

注意事項(xiàng):

  • 錯(cuò)誤邊界組件只能捕獲自己的子組件中的錯(cuò)誤,所以我們一般會(huì)專門制作一個(gè)需要在某個(gè)子組件錯(cuò)誤時(shí)進(jìn)行渲染的備用組件,類似與在vue中如果異步組件的Promise返回的是一個(gè)錯(cuò)誤狀態(tài)就顯示注冊(cè)到錯(cuò)誤狀態(tài)下的組件
  • 在開發(fā)環(huán)境下就算渲染出來(lái)了備用組件,依然還是會(huì)彈出React自己的報(bào)錯(cuò)頁(yè)面,生產(chǎn)環(huán)境下不會(huì),所以說(shuō)這些錯(cuò)誤邊界組件主要是為生產(chǎn)環(huán)境使用的

無(wú)法捕獲異常的情況

下面這些情況下錯(cuò)誤邊界無(wú)法捕獲到異常:

  • 事件處理
  • 異步代碼
  • 服務(wù)端渲染
  • 自身拋出來(lái)的錯(cuò)誤

對(duì)于錯(cuò)誤邊界無(wú)法捕獲的異常,可以使用jstry...catch...語(yǔ)法

舉例:我們?cè)诋?dāng)前組件自身內(nèi)部通過(guò)點(diǎn)擊事件觸發(fā)一個(gè)錯(cuò)誤拋出來(lái)實(shí)現(xiàn)上面兩個(gè)異常捕獲方法所實(shí)現(xiàn)的功能

import React, { Component } from 'react'

export default class ErrorComp extends Component {
    constructor(props){
        super(props)
        this.state = {
            error:null
        }
    }
    handleClick() {
        try {
          throw new Error("我是一個(gè)錯(cuò)誤")  //在try中手動(dòng)制作一個(gè)錯(cuò)誤拋出讓catch被執(zhí)行
        } catch (error) {
          this.setState({ error });
        }
      }
    render() {
        return (
            <div>
                {
                    this.state.error ? <p>{'我是錯(cuò)誤的'}</p> : <p>{'我是正確的'}</p>
                }
                <button onClick={this.handleClick.bind(this)}>按鈕</button>
            </div>            
        )      
    }
}

代碼分析:

我們通過(guò)點(diǎn)擊事件觸發(fā)一個(gè)try…catch語(yǔ)句的執(zhí)行,并且在try中故意拋出一個(gè)錯(cuò)誤,從而修改error的狀態(tài)值,來(lái)控制當(dāng)前組件自己的渲染結(jié)果

總結(jié):

老生命周期函數(shù):

掛載階段:
  1. constructor()

    初始化整個(gè)組件的state 以及調(diào)用super(props),該方法只會(huì)執(zhí)行一次

  2. componentWillMount()

    掛載之前(已經(jīng)棄用,目前React18.2中依然還是可以使用,如果使用會(huì)報(bào)警告)

  3. render()

    只返回需要渲染頁(yè)面結(jié)構(gòu),最好不要包含其它的業(yè)務(wù)邏輯

  4. componentDidMount()

    掛載之后,可以獲取到DOM節(jié)點(diǎn)并操作,服務(wù)器請(qǐng)求,啟用事件監(jiān)聽(tīng),調(diào)用 setState()等都可以在此函數(shù)中

更新階段情況一:state更新時(shí)
  1. shouldComponentUpdate(nextProps,nextState)

    有兩個(gè)參數(shù)nextPropsnextState,表示新的屬性和變化之后的state,返回一個(gè)布爾值,true表示會(huì)觸發(fā)重新渲染,false表示不會(huì)觸發(fā)重新渲染,默認(rèn)返回true,我們通常利用此生命周期來(lái)優(yōu)化React程序性能

  2. componentWillUpdate()

    準(zhǔn)備開始更新(已經(jīng)棄用,目前React18.2中依然還是可以使用,如果使用會(huì)報(bào)警告)

  3. render()

    重新渲染頁(yè)面結(jié)構(gòu)(與掛載階段共享同一個(gè)函數(shù))

  4. componentDidUpdate()

    在組件完成更新后立即調(diào)用,在初始化時(shí)不會(huì)被調(diào)用

更新階段情況二:props更新時(shí)
  1. componentWillReceiveProps(nextProps)

    外部傳入的數(shù)據(jù)發(fā)生變化時(shí)觸發(fā)(父組件修改了一個(gè)自己組件內(nèi)部的數(shù)據(jù),而這個(gè)被修改的數(shù)據(jù)時(shí)有傳遞給子組件的)

  2. shouldComponentUpdate(nextProps,nextState)

    有兩個(gè)參數(shù)nextPropsnextState,表示新的屬性和變化之后的state,返回一個(gè)布爾值,true表示會(huì)觸發(fā)重新渲染,false表示不會(huì)觸發(fā)重新渲染,默認(rèn)返回true,我們通常利用此生命周期來(lái)優(yōu)化React程序性能

  3. componentWillUpdate()

    準(zhǔn)備開始更新(已經(jīng)棄用,目前React18.2中依然還是可以使用,如果使用會(huì)報(bào)警告)

  4. render()

    重新渲染頁(yè)面結(jié)構(gòu)(與掛載階段共享同一個(gè)函數(shù))

  5. componentDidUpdate()

    在組件完成更新后立即調(diào)用,在初始化時(shí)不會(huì)被調(diào)用

卸載階段:

1、componentWillUnmount()

當(dāng)組件卸載時(shí)觸發(fā),我們一般在這個(gè)函數(shù)里去清除一些定時(shí)器,取消網(wǎng)絡(luò)請(qǐng)求,清理無(wú)效的DOM元素等垃圾清理工作。

以上已被棄用的聲明周期函數(shù)
  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate

但是在當(dāng)前React18的版本中依然還是可以使用,但是回報(bào)警告,要求加上前綴UNSAFE_

新生命周期函數(shù)

掛載階段:
  1. constructor()

    初始化整個(gè)組件的state 以及調(diào)用super(props),該方法只會(huì)執(zhí)行一次

  2. getDerivedStateFromProps(nextProps,prevState)

    這是個(gè)靜態(tài)方法,使用時(shí)記得添加static修飾符,在初始掛載及后續(xù)更新時(shí)都會(huì)被調(diào)用,接收兩個(gè)參數(shù)分別是新傳入的props和修改之后的state,它返回一個(gè)對(duì)象來(lái)更新 state,如果返回 null 則不更新任何內(nèi)容。

  3. render

    只返回需要渲染頁(yè)面結(jié)構(gòu),最好不要包含其它的業(yè)務(wù)邏輯

  4. componentDidMount()

    組件裝載之后調(diào)用,可以獲取到DOM節(jié)點(diǎn)并操作,服務(wù)器請(qǐng)求,啟用事件監(jiān)聽(tīng),調(diào)用 setState()等都可以在此函數(shù)中

更新階段
  1. getDerivedStateFromProps()

    此方法在更新和掛載階段都會(huì)調(diào)用

  2. shouldComponentUpdate(nextProps,nextState)

    該方法有兩個(gè)參數(shù)nextProps和nextState,表示新的屬性和變化之后的state,返回一個(gè)布爾值,true表示會(huì)觸發(fā)重新渲染,false表示不會(huì)觸發(fā)重新渲染,默認(rèn)返回true,我們通常利用此生命周期來(lái)優(yōu)化React程序性能

  3. render()

    重新渲染頁(yè)面結(jié)構(gòu)(與掛載階段共享同一個(gè)函數(shù))

  4. getSnapshotBeforeUpdate(prevProps,prevState)

    這個(gè)方法在render之后,componentDidUpdate之前調(diào)用,有兩個(gè)參數(shù)prevProps和prevState,表示之前的屬性和之前的state,這個(gè)函數(shù)有一個(gè)返回值,會(huì)作為第三個(gè)參數(shù)傳給componentDidUpdate,如果你不想要返回值,可以返回null,此生命周期需要與componentDidUpdate搭配使用

  5. componentDidUpdate(prevProps,prevState,snapshot)

    該方法在getSnapshotBeforeUpdate方法之后被調(diào)用,有三個(gè)參數(shù)prevProps,prevState,snapshot,表示之前的props,之前的state,和snapshot。第三個(gè)參數(shù)是getSnapshotBeforeUpdate返回的,如果觸發(fā)某些回調(diào)函數(shù)時(shí)需要用到 DOM 元素的狀態(tài),則將對(duì)比或計(jì)算的過(guò)程遷移至getSnapshotBeforeUpdate,然后在 componentDidUpdate中統(tǒng)一觸發(fā)回調(diào)或更新?tīng)顟B(tài)。

卸載階段
  1. componentWillUnmount()

    當(dāng)組件被卸載或者銷毀了就會(huì)調(diào)用,我們可以在這個(gè)函數(shù)里去清除一些定時(shí)器,取消網(wǎng)絡(luò)請(qǐng)求,清理無(wú)效的DOM元素等垃圾清理工作。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-833280.html

到了這里,關(guān)于【學(xué)習(xí)前端第七十四課】React生命周期的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【react全家桶學(xué)習(xí)】react的 (新/舊) 生命周期(重點(diǎn))

    【react全家桶學(xué)習(xí)】react的 (新/舊) 生命周期(重點(diǎn))

    目錄 生命周期(舊)?? 掛載時(shí)的生命周期 ? constructor(props) ? componentWillMount()-------------新生命周期已替換? ? render() ? componentDidMount()---??組件掛載完畢的鉤子 渲染時(shí)的生命周期 componentWillReceiveProps (nextProps)------------------新生命周期已替換?,父組件渲染的時(shí)候子

    2024年02月12日
    瀏覽(20)
  • 前端學(xué)習(xí)--Vue(4) 生命周期

    前端學(xué)習(xí)--Vue(4) 生命周期

    一個(gè)組件從創(chuàng)建-運(yùn)行-銷毀的真?zhèn)€階段,強(qiáng)調(diào)的是一個(gè)時(shí)間段 ?1.1.1 創(chuàng)建 (只執(zhí)行一次) created() 階段任務(wù):最早可以使用methods中的方法發(fā)起ajax請(qǐng)求獲取數(shù)據(jù),并將數(shù)據(jù)掛載到data中 mounted() 階段任務(wù):最早可以操作dom元素 1.1.2 運(yùn)行 (最少0次,最多n次) beforeUpdate() 觸發(fā)時(shí)機(jī):

    2024年02月07日
    瀏覽(28)
  • 【React】: React的生命周期

    【React】: React的生命周期

    生命周期的每個(gè)階段總是伴隨著一些方法的調(diào)用,這些方法就是生命周期的鉤子函數(shù) 鉤子函數(shù)的作用:為開發(fā)人員在不同操作階段提供了十幾 只有 類組件 才有生命周期 ? 生命周期的圖片: ?同時(shí)有: ?編寫以下代碼,從而驗(yàn)證constructor,render,componentDidMount的順序: 在開發(fā)者

    2024年02月08日
    瀏覽(18)
  • 【react】react生命周期鉤子函數(shù):

    【react】react生命周期鉤子函數(shù):

    一、生命周期概念: 生命周期:簡(jiǎn)單來(lái)說(shuō)就是一個(gè)事物從出生到消亡的過(guò)程就是生命周期,在React中的生命周期,就是組件從創(chuàng)建、掛載到頁(yè)面再到卸載組件的過(guò)程。 意義:生命周期有助于理解組件運(yùn)行方式、完成復(fù)雜組件功能、分析組件中間問(wèn)題產(chǎn)生的原因等。 生命周期鉤子函數(shù)

    2024年02月14日
    瀏覽(25)
  • react 生命周期講解

    當(dāng)涉及到React組件的創(chuàng)建、更新和銷毀過(guò)程時(shí),React的生命周期方法起到了至關(guān)重要的作用。正確地理解和使用這些生命周期方法可以幫助我們?cè)诓煌碾A段執(zhí)行特定的操作,從而實(shí)現(xiàn)更好的組件控制和優(yōu)化。 1. 掛載階段(Mounting) 在組件被創(chuàng)建并添加到DOM中時(shí),以下生命周

    2024年02月07日
    瀏覽(66)
  • React生命周期(新-舊)

    React生命周期(新-舊)

    ① 是什么? 組件 從創(chuàng)建 到掛載頁(yè)面上運(yùn)行,再到組件不用時(shí) 卸載 的過(guò)程,叫生命周期,只有 類組件才有生命周期 。 ②作用 學(xué)習(xí)組件的生命周期,有助于理解組件的運(yùn)行方式,完成更復(fù)雜的組件更能、分析組件錯(cuò)誤原因等。 ① 是什么? 生命周期的每個(gè)階段總是伴隨著一

    2024年02月11日
    瀏覽(27)
  • 淺談React生命周期

    在React中,組件的生命周期是指組件從創(chuàng)建到銷毀的整個(gè)過(guò)程中所經(jīng)歷的一系列階段。React 16.3版本之前,組件的生命周期可以分為三個(gè)階段:掛載階段(Mounting)、更新階段(Updating)和卸載階段(Unmounting)。但是自React 16.3版本起,官方推出了新的生命周期方法,將原有的一

    2024年02月10日
    瀏覽(19)
  • React 生命周期新舊對(duì)比

    React 生命周期新舊對(duì)比

    React16.4版本之后使用了新的生命周期,它使用了一些新的生命周期鉤子(getDerivedStateFromProps、getSnapshotBeforeUpdate),并且即將廢棄老版的3個(gè)生命周期鉤子(componentWillMount、componentWillReceiveProps、componentWillUpdate)。 因?yàn)镽eact 16引入了 Fiber機(jī)制 ,把 同步 的渲染流程進(jìn)化為了 異步

    2024年02月11日
    瀏覽(22)
  • React 組件生命周期

    React 組件生命周期

    Mounting(掛載):已插入真實(shí) DOM Updating(更新):正在被重新渲染 Unmounting(卸載):已移出真實(shí) DOM Mounting階段叫掛載階段,伴隨整個(gè)虛擬DOM的聲明。它里面有四個(gè)小的生命周期函數(shù),分別是: constructor:初始化屬性 componentWillMount:在組件即將被掛載到頁(yè)面的時(shí)候執(zhí)行 render:頁(yè)面s

    2024年02月10日
    瀏覽(48)
  • react 生命周期方法

    react 生命周期方法

    每個(gè)組件都包含 “生命周期方法”,你可以重寫這些方法,以便于在運(yùn)行過(guò)程中特定的階段執(zhí)行這些方法。你可以使用此生命周期圖譜作為速查表。在下述列表中,常用的生命周期方法會(huì)被加粗。其余生命周期函數(shù)的使用則相對(duì)罕見(jiàn)。 掛載 當(dāng)組件實(shí)例被創(chuàng)建并插入 DOM 中時(shí)

    2024年02月13日
    瀏覽(25)

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包