摘要:另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數中能做什么,不能或盡量不要做什么。各個生命周期函數介紹及使用經驗。獲取組件的初始內部狀態在中。該聲明周期函數可能在兩種情況下被調用組件接收到了新的屬性。
文章標題總算是可以正常一點了……
通過之前的文章我們已經知道:在 React 體系中所謂的 "在 JavaScript 中編寫 HTML 代碼" 指的是 React 擴展了 JavaScript 的語法,也就是 JSX。JSX 語法中可以以類似 HTML 語法的方式使用 React 組件,從而編寫 React 組件就有一種創造一個新的 HTML 標簽的體驗。
上一篇文章《玩轉 React(四)- 創造一個新的 HTML 標簽》介紹了如何來創建一個 React 組件,以及組件的屬性。了解到組件的視圖是屬性的映射,通過改變組件屬性可以觸發組件重新渲染,從而改變組件的視圖。其實組件的視圖并不僅僅是由屬性映射來的,本篇將介紹另一種可以觸發組件重新渲染的方式,即組件的內部狀態(state),嚴格來說組件的視圖是由屬性和內部狀態映射而來的,即:view = f(props, state),跟屬性類似,狀態的改變也會觸發組件重新渲染,只不過狀態是組件內部基于自身邏輯或者用戶事件自己維護的,而不是由外部輸入的。
另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數中能做什么,不能或盡量不要做什么。
內容摘要ReactDOM.render 在一個單頁面 web 應用中通常只調用一次。
組件可以通過 setState 改變內部狀態 state 來更新視圖。
setState 多數情況下是異步的。
不要直接使用當前 state 的值生成下一個 state。
不要直接通過 this.state 修改 state。
組件生命周期流程圖。
各個生命周期函數介紹及使用經驗。
以上是本文的內容摘要,如果你已經知道我要說的是什么,那么就沒有必要繼續看下去了,節約時間。
組件的內部狀態此前,我們已經了解到可以通過 ReactDOM.render(
下面是官方文檔中一個展示時鐘的例子,我簡單改造了下:
https://codepen.io/Sarike/pen...
例子中定義了一個 Clock 組件,組件接收一個 time 屬性,在組件外部通過 setInterval 周期性地調用 ReactDOM.render 不斷更新 Clock 的屬性并重新渲染。
然而在很多實際場景中,對于一個時鐘組件,我們希望它有更好的封裝性和復用性,也就是說我們希望只調用一次 ReactDOM.render(
要達到這個目的,就需要組件的內部狀態來支持。組件有一個特殊的屬性 state 用來保存組件的內部狀態。用戶可以通過 this.setState(statePatch) 來更新組件的狀態,組件的狀態更新后會重新執行 render 方法來更新視圖,上面的例子使用內部狀態改造后:
https://codepen.io/Sarike/pen...
這樣 Clock 作為一個完整的時鐘組件就可以自己來更新自己了,上篇文中也有提到過,如果想要使用組件的內部狀態,那組件必須以類繼承的方式來定義,而不能使用函數式組件。所以說,函數式組件經常也被稱作是無狀態組件(stateless)。
上面例子中有用到 componentDidMount 和 componentWillUnmount 兩個函數,它們是組件的生命周期函數,本文的后半部分將會介紹,這倆函數分別在組件掛載到頁面上和組件將要從頁面上移除時調用。
改造后的例子,我們只需要調用一次 ReactDOM.render 即可,在實際的項目中,一個完整的單頁面 web 應用,也只需要調用一次 ReactDOM.render 方法把根組件掛載到頁面中即可,剩下的工作就都放心地交給 React 就行了。
初始化組件內部狀態在創建一個擁有內部狀態的組件時,我們需要對內部狀態進行初始化,即設置組件最初的狀態是什么。做法很簡單,就是在構造函數 constructor 中設置 state 屬性就可以了。如下所示:
class MyComponent extends React.Component { constructor(props) { super(props); // 這行代碼不能少哦 this.state = { name: "Lucy" } } }setState 大多數情況下是異步的
setState 多數情況下是異步的,異步意味著通過 setState 更新組件狀態后,不能立刻通過 this.state 來獲取到更新之后的值,另外當連續多次調用 setState 來更新同一個字段時,只有最后一次更新才會生效。如下示例:
https://codepen.io/Sarike/pen...
如果希望上面示例代碼正常工作,你需要通過回調函數的方式來生成下一個 state,如下所示:
this.setState(preState => ({value: preState.value + 1})); this.setState(preState => ({value: preState.value + 2})); this.setState(preState => ({value: preState.value + 3}));
所以,直接基于當前 state 的值,生成一下個 state 是不靠譜的,但是很多不清楚這一點的同學基本上都是這么做的,因為寫起來簡單嘛,而且貌似也沒有什么問題。這是因為很多情況下,業務邏輯沒有那么復雜,基本不會頻繁調用 setState 。但是這確實是一個隱患,如果在項目初期不注意規避,等項目復雜到一定程度以后,可能會出現難以排查的BUG。
那為什么說多數情況下是異步的呢?難道有些情況下不是異步的嗎?是的,實際上只有在 React 能控制的事件處理過程中調用的 setState 才是異步的,如:生命周期函數,React 內置的如 button,input 等組件的事件處理函數。在多數的情況下我們只需要在這些地方控制我們的組件就夠了,所以說大多數情況下 setState 是異步的。
在某些特殊的組件中,可能需要通過 addEventListener 來設置某些 DOM 的事件處理函數,在這種通過原生的 JS API 來設置的事件處理過程調用 setState 就是同步的,會立即更新 this.state。另外還有 setInterval、setTimeout 等原生 API 的回調函數也是如此。
參考:https://www.zhihu.com/questio...
不要直接通過 this.state 來更新組件狀態這一點跟屬性類似,直接通過 this.state 修改組件狀態,組件狀態被修改了,但并不會觸發組件的重新渲染。這樣就會導致組件視圖與狀態不一致。
生命周期函數一個組件被我們創造到這個世界上之后,在使用它時,它的每個實例都是有一定生命周期的,下面這張圖說明了一個組件實例的生命周期:
圖片來源:https://tylermcginnis.com/an-...,這張圖略微有點老,不過結合下文來看也沒什么問題。
下面我們來解釋一下上面這張圖。
組件初始化:constructor我們定義的每一個組件,都是一個類(class),這些類被實例化后才能作為 React DOM 中的一個節點渲染到頁面上。所以,當我們通過 ReactDOM.render 或者在某個組件中通過 JSX 表達式將一個組件第一次渲染到頁面上時,組件首先要做的就是對組件進行實例化。
實例化主要做的事情:
創建一個組件的實例對象(也就是 Element,通常對應一個JSX表達式,如:
獲取組件的默認屬性。
獲取組件的初始內部狀態(在 constructor 中 this.state = xxxx;)。
componentWillMount在組件被渲染到頁面上之前執行,在組件的整個生命周期內只執行一次。在這里可以調用 setState 更新內部狀態,但是更推薦將這里的狀態更新操作放到 constructor 中。
該函數執行完后會立馬執行 render 方法并將組件渲染到頁面上。所以,在這里執行 setState 不會觸發額外的渲染過程,因為這是沒有必要的。
componentDidMount組件被渲染到頁面上后立馬執行,在組件的整個生命周期內只執行一次。這個時候是做如下操作的好時機:
某些依賴組件 DOM 節點的操作。
發起網絡請求。
設置 setInterval、setTimeout 等計時器操作。
在這里可以調用 setState 更新組件內部狀態,且會觸發一個重新渲染的過程,即會重新執行 render 方法并更新視圖。
componentWillReceivePropscomponentWillReceiveProps(nextProps)
該聲明周期函數可能在兩種情況下被調用:
組件接收到了新的屬性。新的屬性會通過 nextProps 獲取到。
組件沒有收到新的屬性,但是由于父組件重新渲染導致當前組件也被重新渲染。
你只要知道,當該函數被調用時,并不一定是因為屬性發生了變化。
在這里也可以調用 setState 更新組件的內部狀態,同樣也不會觸發額外的重新渲染操作,React 會聰明地用更新后的屬性和內部狀態進行一次重新渲染。
shouldComponentUpdateshouldComponentUpdate(nextProps, nextState)
這是一個詢問式的生命周期函數,所以該函數需要一個返回值 true/false,如果為 true,組件將觸發重新渲染過程,如果為 false 組件將不會觸發重新渲染。因此,合理地利用該函數可以一定程度節省開銷,提高系統的性能。
此處不能調用 setState 更新組件的狀態。
由于組件屬性或者內部狀態被改變時都觸發組件重新渲染,所以該函數接受兩個參數:新的屬性(nextProps)、新的狀態(nextState)。
在處理該聲明周期函數時,切記要兼顧屬性和狀態,不能只顧其一,不然很容易踩坑。例如:某位同學只依據屬性來判斷是否觸發重新渲染,而忽略了內部狀態,這樣就導致你無論如何 setState,組件視圖都不能正常更新。
在上篇文章中我們提到類繼承方式定義組件時說到,React 提供了兩個基類,一個是 Component,另一個是 PureComponent,兩者的差別就在于后者已經幫我們簡單實現了一下 shouldComponentUpdate 函數,當屬性和狀態都沒有發生變化時返回 false 以避免額外的開銷。
但是比對過程出于性能考慮,只是進行淺比對,也就是只比對對象的第一級字段,而且是否發生變化是通過 Object.is 方法類判斷的。所以會導致有時候發生變化了組件沒有更新,沒有變化卻觸發了重新渲染過程。這個在這里不再贅述,想深入探討可以掃描問候的二維碼加我微信好友(我的微信:leobaba88)。
componentWillUpdate當組件 shouldComponentUpdate 返回 true 或者調用 forceUpdate 時將觸發此函數。
該函數中不能調用 setState 更新組件狀態,當你想這么做的時候,你可以考慮將它移到 componentWillReceiveProps 函數里。
該函數在函數第一次渲染的時候不會執行。
componentDidUpdatecomponentDidUpdate(prevProps, prevState)
在組件重新渲染過程中,重新執行 render 方法并更新組件視圖后立即執行該函數。類似組件第一次渲染過程中的 componentDidMount,該函數在第一次渲染時不會執行。
在此處是做這些事情的好時機:
執行依賴新 DOM 節點的操作。
依據新的屬性發起新的網絡請求。(但是此處一定要格外謹慎,一定要在確認屬性變化后再發起網絡請求,不然極有可能進入死循環:didUpdate -> ajax -> changeProps -> didUpdate -> ...)。
componentWillUnmount當組件被從頁面中移除之前調用,此時是清理戰場的好時機,如清理定時器、終止網絡請求等。
componentDidCatchcomponentDidCatch(error, info)
這是 React 16 新加入的一個生命周期函數。定義該生命周期函數的組件將會成為一個錯誤邊界,錯誤邊界這個詞非常形象,它可以有效地將錯誤限制在一個有限的范圍內,而不會導致整個應用崩潰,防止一顆耗子屎壞了一鍋湯。
錯誤邊界組件,可以捕獲其整個子組件樹內發生的任何異常,但是卻不能捕獲自身的異常。
下面是官方的一個示例,大家感受下:
https://codepen.io/gaearon/pe...
最后(微信群)這篇文章來的有點慢,非常抱歉。
另外為了方便大家閱讀,我將所有文章的鏈接更新到第一篇文章 《玩轉React(一)- 前言》 中。
文字的表現范圍畢竟有限,為了方便大家交流,我建了一個微信群,對 React 感興趣的同學可以進群一起交流、學習,由于微信群邀請的時間限制,大家可以先掃描下面二維碼,加我好友,我拉大家進群:
我的微信:leobaba88
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/91996.html
摘要:另外本文中會介紹一個通過類繼承方式定義的組件的生命周期,以及在各個生命周期函數中能做什么,不能或盡量不要做什么。各個生命周期函數介紹及使用經驗。獲取組件的初始內部狀態在中。該聲明周期函數可能在兩種情況下被調用組件接收到了新的屬性。 文章標題總算是可以正常一點了…… 通過之前的文章我們已經知道:在 React 體系中所謂的 在 JavaScript 中編寫 HTML 代碼 指的是 Rea...
摘要:本人計劃編寫一個針對中初級前端開發者學習的系列教程玩轉。使用的原因是新的語言規范開發效率更高代碼更優雅,尤其是基于開發的項目。其次也是目前特別流行的一個前端框架,截止目前,上有將近萬,國內一二線互聯網公司都有深度依賴開發的項目。 本人計劃編寫一個針對中初級前端開發者學習 React 的系列教程 - 《玩轉 React》。 文章更新頻率:每周 1 ~ 2 篇。 目錄 玩轉 React(...
摘要:屬性是一個組件的外部輸入。只會在開發模式下進行屬性類型檢查,當代碼進行生產發布后,為了減少額外的性能開銷,類型檢查將會被略過。某個類的實例枚舉,屬性值必須為其中的某一個值。屬性為一個數組,且數組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發方式》 中有說到 React 有很爽的一點就是給我們一種創造 HTML 標簽的能力,那么今天這篇文章就詳細講解下 React 是如何提供這...
摘要:屬性是一個組件的外部輸入。只會在開發模式下進行屬性類型檢查,當代碼進行生產發布后,為了減少額外的性能開銷,類型檢查將會被略過。某個類的實例枚舉,屬性值必須為其中的某一個值。屬性為一個數組,且數組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發方式》 中有說到 React 有很爽的一點就是給我們一種創造 HTML 標簽的能力,那么今天這篇文章就詳細講解下 React 是如何提供這...
閱讀 1176·2023-04-26 00:34
閱讀 3348·2023-04-25 16:47
閱讀 2110·2021-11-24 11:14
閱讀 3093·2021-09-26 09:55
閱讀 3685·2019-08-30 15:56
閱讀 3211·2019-08-29 16:57
閱讀 1903·2019-08-26 13:38
閱讀 2663·2019-08-26 12:22