ReactJS入門 - ClassComponent 生命週期函數 - 筆記長也NotesHazuya

ReactJS入門 - ClassComponent 生命週期函數

2021-06-17 20:31:00   ReactJS

何謂生命週期

大部分的前端框架都會有元件的生命週期,也就是渲染動作的流程。在 React 的元件之中,總共有三種不同時機:

  1. 元件安裝時(mount)
  2. 元件更新時(update)
  3. 元件移除時(unmount)

在這三個週期之中,個別提供了不同的函式可以使用,並且可以定義每個週期要做什麼事情,下面我們搭配一些很簡單的例子來說明各個階段所提供的不同函數,並說明可能的使用時機。

元件安裝時(mount)

constructor()

建構子應該不用說明太多,一但元件被安裝的時候會先執行建構子建立元件以及其他資料宣告、初始值、準備或是綁定函式等⋯⋯。

static getDerivedStateFromProps(props, state)

這個函式會在建構子之後才被執行,由於 props 以及 state 都會在建構子執行後才被建立,因此這個函式裡面最常使用 props 的值來設定 state 之類的動作,以下延續之前的範例來舉一個小範例:

先在 App.js 加上 getDerivedStateFromProps 這個函式

    static getDerivedStateFromProps(props, state){
      if( props.name === "ted" ){
        return { percent:100 }
      }
    }

要注意這個函式是 static 的(不會隨不同物件而變化)所以宣告時記得加上 static,而由於是 static (不會有自己的屬性)所以要將這個元件的 props 以及 state 傳進去,而在函式裡面操作 props 不用加上 this,而要 setState 則是透過這個函式的回傳值設定。

然後把 index.js 的 ReactDom.render 改成這樣

ReactDOM.render(
  <>
    <NavBar>
      <App name="ted"></App>
    </NavBar>
  </>
   ,
  document.getElementById('root')
);

我們在 getDerivedStateFromProps 來做如果傳進去的 name 是 ted,就把 percent 改成 100。

render()

render 是渲染畫面前最後一個被呼叫的函式,但是這個階段還只是準備渲染而非真正的渲染完成了,因此不要在這裡做任何的 DOM 操作,因為還沒渲染自然會出錯。

componentDidMount()

componentDidMount 是第一次渲染完成後唯一被觸發的生命週期函式。從這裡開始,畫面已經被渲染上去,因此可以進行 DOM 操作,例如先在 App.js 加上這個函式:

    componentDidMount(){
      document.getElementById('btn').innerHTML="加加加";
    }

這個函式不是靜態的,所以不必加上 static,也不必帶入參數。當中如果呼叫 state 與 props 也都要加上 this。

然後把 render 中的 button 修改一下加上 id 這個屬性

    render(){
      return(
        <div>
          <h1> { this.state.percent } </h1>
          <button id="btn" onClick={ this.plusPercent }>++</button>
        </div>
        );
      }

執行後會發現按鈕上的字被改成“加加加”了

而這個函式另外一個比較常被用來做的事情是透過 http request 跟伺服器的 API 拿資料,下面在 App.js 建立一個函式模擬一個 ajax 請求:

    ajaxSimulator(){
      setTimeout(()=>{this.setState({isGetData:true, name:"John"})},3000)
  }

在 componentDidMount 呼叫一下上面的函式

    componentDidMount(){
      document.getElementById('btn').innerHTML="加加加";
      this.ajaxSimulator()
    }

然後再修改一下 render 的部分

    render(){
      if( this.state.isGetData===false ){
        return(         
        <div>
          <h1> Loading... </h1>
          <button id="btn" onClick={ this.plusPercent }>++</button>
        </div> 
        )
      }else{
        return(
        <div>
          <h1> { this.state.percent } </h1>
          <button id="btn" onClick={ this.plusPercent }>++</button>
        </div>
        );
      }

      }

這樣就會發現到在取得資料之前會顯示 Loading...,而取得後會顯示訂購人:John。

元件更新時(update)

static getDerivedStateFromProps(props, state)

這個函數與前面 mount 階段的是共用同一個靜態函數的,這個函數功能是取得 props 的值去設定 state 的值,與 mount 的功能差不多,而這個函數事實上只要畫面被重新渲染就會被執行。

render()

與前面一樣,當畫面被更新時也會呼叫這個函數。

shouldComponentUpdate(nextProps, nextState)

這個函數會確認畫面是不是真的要 update,這個函數 return 一個布林值,如果回傳 false 畫面就不會被更新,不會觸發接下來的畫面渲染(render)階段以及其他 update 函數。預設回傳 true。

在這個函數階段, this.props 以及 this.state 是被更新之前的,而新的 props 以及 新的 state 會以 nextProps 以及 nextState 存在於參數之中,可以利用這四個變數比較。

getSnapshotBeforeUpdate(prevProps, prevProps)

這個函數會在 render 已經準備好渲染的東西以及真的進行渲染之間被呼叫,並記錄舊的狀態 return 到 componentDidUpdate 當中,如果沒有要 return 回去則 return null。

componentDidUpdate(prevProps, prevState, snapshot)

這個函數會在畫面更新後執行,把更新後想做的事情放在這裡。這個函數是唯一以及最後在 DOM 實際被更新後而被執行的函數。在這個週期中, this.props 以及 this.state 是更新過後的,舊的 props 以及 state 會以參數 prevProps 以及 prevState 存在,而 snapshot 是  getSnapshotBeforeUpdate 回傳的。

這個函數要注意,如果在這個函數中又再次更新了 props 或是 state 的值,就會重新進入 update 階段。所以如果要更新某個 state 的值,必須要與前次的值進行比較,才不會使畫面不斷更新成為無窮迴圈。

元件移除時(unmount)

componentWillUnmount()

這格函數是這個週期的唯一一個函數,這個函數會在元件要被移除時執行。通常這個函式會與前面的  componentDidMount 一起使用,當在 componentDidMount 操作 DOM 產生了某種元素或是建立了監聽事件、setInterval 之類的東西,在元件被移除時並不會自動一起清除,所以可能導致增加的元素殘留在畫面、Interval 或是監聽事件重複,此時可以透過這個函數來移除。

例如我們建立了一個監聽事件

clicked(){
    console.log("點")
}

componentDidMount(){
    window.addEventListener('mousedown', this.clicked)
}

為了避免重複建立監聽事件,可以這樣做

componentWillUnmount(){
    window.removeEventListener('mousedown',this.clicked);
}

結論

1. componentDidMount 會最常使用到例如向 API 拿資料之類的事情,如果不確定畫面渲染一開始要做的事情要放在哪個階段,也許這個階段會最為合適。

2. React 作者有設計了一個網站完整呈現了各種階段的函數,需要的可以參考:https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

3.functional component 也可以利用 ReactHook 的 ReactEffect 來實作生命週期,我們會在後面的張篇介紹。

4. 最後,以下幫各位整理了各個階段常用的函數:

階段 函數與可能的使用時機說明
安裝時

static getDerivedStateFromProps(props, state):

-在渲染前利用 props 的值設定 state。

componentDidMount():

-在畫面完成渲染後要做的事情,如向 API 取得資料,只在第一次渲染後呼叫

更新時

static getDerivedStateFromProps(props, state):

-與上面階段的一樣,用 props 來設定 state,只要畫面一被重新渲染就會被觸發。

componentDidUpdate(prevProps, prevState, snapshot):

-在畫面更新並完成渲染後要做的事情,在畫面一被更新後執行。注意修改 state 要比對新與舊的值,避免進入無窮迴圈。

移除時

componentWillUnmount():

-元件被移除時要做的事情,例如移除事件監聽,在元件被移除時呼叫。

關於作者


長也

資管菸酒生,嘗試成為網頁全端工程師(laravel / React),技能樹成長中,閒暇之餘就寫一些筆記。 喔對了,也愛追一些劇,例如火神跟遺物整理師,推推。最愛的樂團應該是告五人吧(?)