在學習或使用過一陣子React後,你可能會發現一個在setState
方法的特性,如下面這個簡單例子來講明:html
export default class SelectBox extends React.Component { constructor(props) { super(props) this.state={value: ''} } handleChange = (e) => { this.setState({value: e.target.value}) console.log(this.state.value) } render() { return( <div> <select onChange={this.handleChange} value={this.state.value}> <option value="JavaScript" key={1}>JavaScript</option> <option value="Angular2" key={2}>Angular2</option> <option value="React" key={3}>React</option> </select> <h1>{this.state.value}</h1> </div> ) } }
咱們在handleChange
方法中,呼叫setState
來更新選項的值,而後在控制檯中輸出這個值。看起來一切都是很符合邏輯,但你若是一執行就會發現,在控制檯中輸出的this.state.value
,並不會在呼叫setState
方法後當即就變更。像下面的執行的結果圖同樣:react
固然,若是你直接輸出的是e.target.value
,必定是正確的值,但在某些狀況下,咱們要取用的並非這個事件的值,而是要更動事後的state(狀態)值。git
若是要在setState
方法後,直接取用更動後的state
值,正確的使用方式,在官方文件中的說明,須要利用setState
的第二傳參,傳入一個回調(callback)函式,改成像下面這樣的代碼:github
this.setState({value: e.target.value}, function(){ console.log(this.state.value) })
另外一個方式則是用componentDidUpdate()
這個生命週期方法,把肯定state
更新後要執行的代碼放在裏面,以下面的代碼:算法
componentDidUpdate(){ console.log(this.state.value) }
爲何必定要這樣做的主要緣由是:promise
setState
這個方法,它在React中的執行行爲能夠認爲"異步的"異步
雖然setState
並不是使用了setTimeout
或promise的那種進入到事件迴圈(Event loop)的異步執行,但它的執行行爲在React庫中時,的確是異步的,也就是有延時執行的行爲。以官方文件中較精確的說法 - "它不是保證同步的"。async
setState
方法與包含在其中的執行是一個很複雜的過程,這段程式碼從React最初的版本到如今,也有無數次的修改。它的工做除了要更動this.state
以外,還要負責觸發從新渲染(render),這裏面要通過React核心中diff演算法,最終才能決定是否要進行重渲染,以及如何渲染。並且爲了批次與效能的理由,多個setState
呼叫有可能在執行過程當中還須要被合併,因此它被設計以異步的或延時的來進行執行是至關合理的。oop
那麼setState
會在什麼時候以同步的方式來執行,也就是當即更動this.state
?答案是在React庫控制以外時,它就會以同步的方式來執行,在下面兩篇文章中,都有相似的例子:學習
但大部份的使用狀況下,咱們都是使用了React庫中的表單組件,例如select、input、button等等,它們都是React庫中人造的組件與事件,是處於React庫的控制之下,在這個狀況下,setState
就會以異步的方式執行。因此通常來講,咱們會認爲setState
就是異步執行,並非用原始碼來看它是否是有使用像setTimeout
或Promise
之類的方式轉爲JavaScript的異步執行方式,而是以它在React庫的控制之下,以執行行爲與順序來認定。
如下是翻自官方setState原代碼的註解,官網的說明也是相似:
不保證
this.state
會當即更新,因此在調用這個方法後存取this.state
可能會回傳舊的值。不保證呼叫
setState
就會同步地執行,而它們也可能最終被被批量調用(屢次呼叫的狀況下)。你能夠提供額外的回調(callback),回調(callback)將會在setState
實際被完成時被執行。
所以,很早就有開發者提出來關於setState
常令初學者感到怪異的執行狀況,在某些狀況下會形成執行後會看到不連續的結果。除了setState
方法有異步執行的行爲外,它還有幾個被提出來的特殊行爲:
setState
可能會引起沒必要要的渲染(renders)state
自己的設計是沒法直接更改,setState
的設計是用來更動state
值,也會觸發從新渲染(re-render),按照邏輯就是反正無論如何,只要開發者呼叫setState
,React就去做整個視圖的從新渲染就是。因此setState
一定會做從新渲染的執行,只是要如何渲染是由React來決定。
從新渲染(re-render)指的主要是頁面上視圖(View)的從新再呈現,這是React本來的核心設計,但這個設計是有一些問題的。最主要的是state(狀態)並不必定單純只用來記錄與視圖(View)有關的狀態,也有多是某個內部控制用的屬性值,或是隻套用在內部使用的資料。當你改變了這些與視圖無關的state(狀態)值,以如今的React設計來講,照樣要觸發從新渲染的執行過程,這在某些複雜的應用時,因爲形成沒必要要的渲染,也有可能形成效能上的問題。
固然,React提供了shouldComponentUpdate
方法讓開發者能夠自行判斷,自行提供對應的解決方式。也有Performance Tools能夠進行剖析檢測。算得上是一些補強的做法。
setState
沒法徹底掌控應用中全部組件的狀態state
(狀態)是獨立於每一個組件內部的,並且它是個不能直接更動的對象,這個設計固然是爲了要保持組件的封裝與獨立性,但因此若是當要開發一個複雜的應用時,一定須要使用那些能掌控全部組件資料,以及能提供各組件間資料互動的函式庫,例如Flux, Redux或MobX等等。
React組件目前只能透過各類生命週期的方法,與外部資源、計時器或DOM事件來進行掛勾(Hook),這些都沒法直接使用setState
方法來進行管理,所以setState
並無辦法徹底掌控一個應用中全部組件的狀態,它比較像是每一個組件中的都有的一種接口方法,單純要依靠setState
方法來管控整個React應用,徹底是不足夠的。
以上說明參考自這篇文章: 3 Reasons why I stopped using React.setState