ReactJS入門 - ClassComponent 生命週期函數

碼農思考中 / Ted
8 min readSep 23, 2023

何謂生命週期

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

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

元件安裝時(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. 最後,以下幫各位整理了各個階段常用的函數:

Originally published at https://studio-44s.tw.

--

--

碼農思考中 / Ted

全端工程師,Asp.Net MVC、PHP Laravel、ReactJS,隨手記錄各種技術、心得與設計靈感。未來規劃除程式技能外,也想往 UI/UX 設計領域發展。目前文章從舊有網站「筆記長也」搬移過來。業餘接案中:https://studio-44s.tw/