天天看點

ReactJS實戰之生命周期

1 生命周期圖

流程圖

ReactJS實戰之生命周期

結構圖

ReactJS實戰之生命周期

元件初始化:

先調用getDefaultProps()

getInitialState()

render

第一次 render 後,調用 componentWillMount、componentDidMount

之後:componentWillUpdate、componentDidUpdate

這些流程就是為了形成鈎子方法。

更新UI可直接調用 <code>ReactDOM.render()</code> 改變輸出

ReactJS實戰之生命周期

那麼如何使<code>Clock</code>元件真正 可重用和封裝?它将設定自己的計時器,并每秒更新一次。

從封裝時鐘開始

ReactJS實戰之生命周期

然而,它錯過了一個關鍵的要求

Clock設定一個定時器并且每秒更新UI應該是Clock的實作細節

理想情況下,我們寫一次 Clock 然後它能更新自身

ReactJS實戰之生命周期

為實作這個需求,我們需要為<code>Clock</code>元件添加<code>狀态</code>

狀态與屬性十分相似,但狀态是<code>私有的,完全受控于目前元件</code>

局部狀态就是:一個功能隻适用于類

2 将函數轉換為類

将函數元件 <code>Clock</code> 轉換為類

建立一個名稱擴充為 <code>React.Component</code> 的ES6 類

建立一個<code>render()</code>空方法

将函數體移動到 <code>render()</code> 中

在 <code>render()</code> 中,使用 <code>this.props</code> 替換 <code>props</code>

删除剩餘的空函數聲明

ReactJS實戰之生命周期

Clock 現在被定義為一個類而不隻是一個函數

使用類就允許我們使用其它特性,例如局部狀态、生命周期鈎子

3 為一個類添加局部狀态

三步将 date 從屬性移動到狀态中

在<code>render()</code>中使用<code>this.state.date</code> 替代 <code>this.props.date</code>

ReactJS實戰之生命周期

添加一個類構造函數來初始化狀态 <code>this.state</code>

ReactJS實戰之生命周期

注意如何傳遞 <code>props</code> 到基礎構造函數的

ReactJS實戰之生命周期

類元件應始終使用<code>props</code>調用基礎構造函數

從 元素移除 date 屬性

ReactJS實戰之生命周期

稍後将定時器代碼添加回元件本身。

結果如下

ReactJS實戰之生命周期

接下來,我們将使Clock設定自己的計時器并每秒更新一次

4 将生命周期方法添加到類中

在具有許多元件的應用程式中,在銷毀時釋放元件所占用的資源非常重要

每當<code>Clock</code>元件第一次加載到DOM時,我們都想[生成定時器],這在React中被稱為<code>挂載</code>

同樣,每當<code>Clock</code>生成的這個DOM被移除時,我們也會想要[清除定時器],這在React中被稱為<code>解除安裝</code>

我們可以在元件類上聲明特殊的方法,當元件挂載或解除安裝時,來運作一些代碼:

這些方法被稱作生命周期鈎子。

當元件輸出到 DOM 後會執行 componentDidMount() 鈎子,這是一個建立定時器的好地方:

注意我們是将定時器ID儲存在 this 中的

盡管 this.props 是由React本身安裝的以及this.state 有特殊的含義,如果你需要存儲的東西不在資料流中,你可以随意手動向類中添加其他字段(比如定時器ID)。

我們将在 componentWillUnmount()生命周期鈎子中解除安裝計時器

最後,我們實作了每秒鐘執行的 tick() 方法。

它将使用 this.setState() 來更新元件局部狀态:

現在時鐘每秒鐘都會執行。

讓我們快速回顧一下發生了什麼以及調用方法的順序:

當 被傳遞給 ReactDOM.render() 時,React 調用 Clock 元件的構造函數。 由于 Clock 需要顯示目前時間,是以使用包含目前時間的對象來初始化 this.state 。 我們稍後會更新此狀态。

React 然後調用 Clock 元件的 render() 方法。這時 React 了解螢幕上應該顯示什麼内容,然後 React 更新 DOM 以比對 Clock 的渲染輸出。

當 Clock 的輸出插入到 DOM 中時,React 調用 componentDidMount() 生命周期鈎子。 在其中,Clock 元件要求浏覽器設定一個定時器,每秒鐘調用一次 tick()。

浏覽器每秒鐘調用 tick() 方法。 在其中,Clock 元件通過使用包含目前時間的對象調用 setState() 來排程UI更新。 通過調用 setState() ,React 知道狀态已經改變,并再次調用 render() 方法來确定螢幕上應當顯示什麼。 這一次,render() 方法中的 this.state.date 将不同,是以渲染輸出将包含更新的時間,并相應地更新DOM。

一旦Clock元件被從DOM中移除,React會調用componentWillUnmount()這個鈎子函數,定時器也就會被清除。

5 正确地使用狀态

關于 <code>setState()</code> 這裡有三件事情需要知道

如下代碼不會重新渲染元件:

應當使用 setState():

構造器是唯一能夠初始化 <code>this.state</code> 的地方。

6 狀态更新可能異步

React 可以将多個<code>setState()</code> 調用合并成一個調用來提高性能。

因為 <code>this.props</code> 和 <code>this.state</code> 可能是異步更新的,你不應該依靠它們的值來計算下一個狀态。

例如,此代碼可能無法更新計數器:

要修複它,請使用第二種形式的 setState() 來接受一個函數而不是一個對象.

該函數将接收先前的狀态作為第一個參數,将此次更新被應用時的props做為第二個參數:

上方代碼使用了箭頭函數,但它也适用于正常函數:

你可以調用 setState() 獨立地更新它們:

這裡的合并是淺合并,也就是說this.setState({comments})完整保留了this.state.posts,但完全替換了this.state.comments。

因為

7 資料自頂向下流動

父元件或子元件都不能知道某個元件是有狀态還是無狀态,并且它們不應該關心某元件是被定義為一個函數還是一個類。

這就是為什麼狀态通常被稱為局部或封裝。 除了擁有并設定它的元件外,其它元件不可通路。

元件可以選擇将其狀态作為屬性傳遞給其子元件:

這也适用于使用者定義的元件:

FormattedDate 元件将在其屬性中接收到 date 值,并且不知道它是來自 Clock 狀态、還是來自 Clock 的屬性、亦或手工輸入:

這通常被稱為自頂向下或單向資料流。 任何狀态始終由某些特定元件所有,并且從該狀态導出的任何資料或 UI 隻能影響樹中下方的元件。

如果你想象一個元件樹作為屬性的瀑布,每個元件的狀态就像一個額外的水源,它連接配接在一個任意點,但也流下來。

為了表明所有元件都是真正隔離的,我們可以建立一個 App 元件,它渲染三個Clock:

每個 Clock 建立自己的定時器并且獨立更新。

在 React 應用程式中,元件是有狀态還是無狀态被認為是可能随時間而變化的元件的實作細節。 可以在有狀态元件中使用無狀态元件,反之亦然。

繼續閱讀