詳解React setState資料更新機制
為什麼使用setState
在React 的開發過程中,難免ftBfT會與元件的sthttp://www.cppcns.comate打交道。使用過React 的都知道,想要修改state中的值,必須使用內部提供的setState 方法。為什麼不能直接使用賦值的方式修改state的值呢?我們就分析一下,先看一個demo。
class Index extends React.Component { this.state = { count: 0 } onClick = () => { this.setState({ count: 10 }) } render() { return ( <div> <span>{this.state.count}</span> <button onClick={this.onClick}>click</button> </div> ) } }
根據上面程式碼可以看到,點選按鈕後把state 中 count 的值修改為 10。並更新頁面的顯示。所以statftBfTe的改變有兩個作用:對應的值改變 和 **程式設計客棧頁面更新。**要想做到這兩點在react 中 非 setState 不可。 假如說我們把 onClick 的方法內容修改為 this.state.count = 10
並在方法內打印出 this.state
的值,可以看到state的值已經改變。但是頁面並沒有更新到最新的值。 ☆總結一下:
- state 值的改變,目的是頁面的更新,希望React 使用最新的 state來渲染頁面。但是直接賦值的方式並不能讓React監聽到state的變化。
- 必須通過setState 方法來告訴React state的資料已經變化。
☆擴充套件一下:
在vue中,採用的就是直接賦值的方式來更新data 資料,並且Vue也能夠使用最新的data資料渲染頁面。這是為什麼呢? 在vue2中採用的是 Object.defineProperty()方式監聽資料的get 和 set 方法,做到資料變化的監聽 在vue3中採用的是ES6 的 proxy方式監聽資料的變化
setState 的用法
想必所有人都會知道setState 的用法,在這裡還是想記錄一下: setState方法有兩個引數:第一個引數可以是物件直接修改屬性值,也可以是函式能夠拿到上一次的state值。第二個引數是一個可選的回撥函式,可以獲取最新的state值 回撥函式會在元件更新完成之後執行,等價於在 componentDidUpdhttp://www.cppcns.com
生命週期內執行。
- 第一個引數是物件時:如同上文的demo一樣,直接修改state的屬性值
this.setState({ key:newState })
- 第一個引數是函式時:在函式內可以獲取上一次state 的屬性值。
// prevState 是上一次的 state,props 是此次更新被應用時的 props this.setState((prevState,props) => { return { key: prevState.key } })
非同步更新還是同步更新
setState() 將對元件 state 的更改排入佇列,並通知 React 需要使用更新後的 state 重新渲染此元件及其子元件。這是用於更新使用者介面以響應事件處理器和處理伺服器資料的主要方式 將 setState() 視為請求而不是立即更新元件的命令。為了更好的感知效能,React 會延遲呼叫它,然後通過一次傳遞更新多個元件。React 並不會保證 state 的變更會立即生效。
先修改一下上面的程式碼,如果在onClick 方法中連續呼叫三次setState,根據上文可知 setState是一個非同步的方式,每次呼叫只是將更改加入佇列,同步呼叫的時候只會執行最後一次更新,所以結果是1而不是3。
onClick = () => { const { count } = this.state this.setState({ count: count + 1 }) this.setState({ count: count + 1 }) this.setState({ count: count + 1 }) }
可以把上面程式碼理解為 Object.assign()
方法,
Object.assign( state,{ count: state.count + 1 },{ count: state.count + 1 } )
如果第一個引數傳入一個函式,連續呼叫三次,是不是和傳入物件方式的結果是一樣的呢?
onClick = () => { this.setState((prevState,props) => { return { count: prevState.count + 1 } }) this.setState((prevState,props) => { return { count: prevState.count + 1 } }) }
結果和傳入物件的方式大相徑庭,使用函式的方式就能夠實現自增為3的效果。這又是為什麼呢? 在函式內能夠拿到最新的state 和 props值。由上文可知 setState 的更新是分批次的,使用函式的方式確保了當前state 是建立在上一個state 之上的,所以實現了自增3的效果。
☆總結一下: 為什麼setState 方法是非同步的呢?
- 可以顯著的提升效能,react16 引入了 Fiber 架構,Fiber 中對任務進行了劃分和優先順序的分類,優先處理優先順序比較高的任務。頁面的響應就是一個優先順序比較高任務,所以如果setState是同步,那麼更新一次就要更新一次頁面,就會阻塞到頁面的響應。最好的辦法就是獲得到多個更新,之後進行批量的更新。只更新一次頁面。
- 如果同步更新state,但是還沒有執行render 函式,那麼state 和 props 就不能夠保持同步。
**是不是所有的setState 都是非同步的形式呢?**答案是 否!!!在React 中也會存在setState 同步的場景
onClick = () => { this.setState({ count: this.state.count + 1 }) console.log(this.state) setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log(this.state) },0) }
上面的程式碼會打印出**0,2。**這又是為什麼呢?其實React 中的 setState 並不是嚴格意義上的非同步函式。他是通過佇列的延遲執行實現的。使用 isBatchingUpdates
判斷當前的setState 是加入到更新佇列還是更新頁面。當 isBatchingUpdates=ture
是加入更新佇列,否則執行更新。
知道了React 是使用 isBatchingUpdates
來判斷是否加入更新佇列。那麼為什麼在 setTimeout
事件中 isBatchingUpdates
值為 false
? 原因就是在React中,對HTML的原生事件做了一次封裝叫做**合成事件。**所以在React自己的生命週期和合成事件中,可以控制 isBatchingUdates
的值,可以根據值來判斷是否更新頁面。而在宿主環境提供的原生事件中(即非合成事件),無法將 isBatchingUpdates
的值置為 false,所以就會立即執行更新。
☆所以setState 並不是有同步的場景,而是在特殊的場景下不受React 的控制 **
總結
setState 並不是單純的同步函式或者非同步函式,他的同步和非同步的表現差異體現在呼叫的場景不同。在React 的生命週期和合成事件中他表現為非同步函式。而在DOM的原生事件等非合成事件中表現為同步函式。
以上就是詳解React setState資料更新機制的詳細內容,更多關於React setState資料更新機制的資料請關注我們其它相關文章!