詳解React setState數(shù)據(jù)更新機(jī)制
為什么使用setState
在React 的開發(fā)過程中,難免會與組件的state打交道。使用過React 的都知道,想要修改state中的值,必須使用內(nèi)部提供的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> ) } }
根據(jù)上面代碼可以看到,點(diǎn)擊按鈕后把state 中 count 的值修改為 10。并更新頁面的顯示。所以state的改變有兩個作用:對應(yīng)的值改變 和 **頁面更新。**要想做到這兩點(diǎn)在react 中 非 setState 不可。 假如說我們把 onClick 的方法內(nèi)容修改為 this.state.count = 10
并在方法內(nèi)打印出 this.state
的值,可以看到state的值已經(jīng)改變。但是頁面并沒有更新到最新的值。 ☆總結(jié)一下:
- state 值的改變,目的是頁面的更新,希望React 使用最新的 state來渲染頁面。但是直接賦值的方式并不能讓React監(jiān)聽到state的變化。
- 必須通過setState 方法來告訴React state的數(shù)據(jù)已經(jīng)變化。
☆擴(kuò)展一下:
在vue中,采用的就是直接賦值的方式來更新data 數(shù)據(jù),并且Vue也能夠使用最新的data數(shù)據(jù)渲染頁面。這是為什么呢? 在vue2中采用的是 Object.defineProperty() 方式監(jiān)聽數(shù)據(jù)的get 和 set 方法,做到數(shù)據(jù)變化的監(jiān)聽 在vue3中采用的是ES6 的 proxy 方式監(jiān)聽數(shù)據(jù)的變化
setState 的用法
想必所有人都會知道setState 的用法,在這里還是想記錄一下: setState方法有兩個參數(shù):第一個參數(shù)可以是對象直接修改屬性值,也可以是函數(shù)能夠拿到上一次的state值。第二個參數(shù)是一個可選的回調(diào)函數(shù),可以獲取最新的state值 回調(diào)函數(shù)會在組件更新完成之后執(zhí)行,等價于在 componentDidUpdate
生命周期內(nèi)執(zhí)行。
- 第一個參數(shù)是對象時:如同上文的demo一樣,直接修改state的屬性值
this.setState({ key:newState })
- 第一個參數(shù)是函數(shù)時:在函數(shù)內(nèi)可以獲取上一次state 的屬性值。
// prevState 是上一次的 state,props 是此次更新被應(yīng)用時的 props this.setState((prevState, props) => { return { key: prevState.key } })
異步更新還是同步更新
setState() 將對組件 state 的更改排入隊列,并通知 React 需要使用更新后的 state 重新渲染此組件及其子組件。這是用于更新用戶界面以響應(yīng)事件處理器和處理服務(wù)器數(shù)據(jù)的主要方式 將 setState() 視為請求而不是立即更新組件的命令。為了更好的感知性能,React 會延遲調(diào)用它,然后通過一次傳遞更新多個組件。React 并不會保證 state 的變更會立即生效。
先修改一下上面的代碼,如果在onClick 方法中連續(xù)調(diào)用三次setState,根據(jù)上文可知 setState是一個異步的方式,每次調(diào)用只是將更改加入隊列,同步調(diào)用的時候只會執(zhí)行最后一次更新,所以結(jié)果是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 }, { count: state.count + 1 } )
如果第一個參數(shù)傳入一個函數(shù),連續(xù)調(diào)用三次,是不是和傳入對象方式的結(jié)果是一樣的呢?
onClick = () => { this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) this.setState((prevState, props) => { return { count: prevState.count + 1 } }) }
結(jié)果和傳入對象的方式大相徑庭,使用函數(shù)的方式就能夠?qū)崿F(xiàn)自增為3的效果。這又是為什么呢? 在函數(shù)內(nèi)能夠拿到最新的state 和 props值。由上文可知 setState 的更新是分批次的,使用函數(shù)的方式確保了當(dāng)前state 是建立在上一個state 之上的,所以實現(xiàn)了自增3的效果。
☆總結(jié)一下: 為什么setState 方法是異步的呢?
- 可以顯著的提升性能,react16 引入了 Fiber 架構(gòu),F(xiàn)iber 中對任務(wù)進(jìn)行了劃分和優(yōu)先級的分類,優(yōu)先處理優(yōu)先級比較高的任務(wù)。頁面的響應(yīng)就是一個優(yōu)先級比較高任務(wù),所以如果setState是同步,那么更新一次就要更新一次頁面,就會阻塞到頁面的響應(yīng)。最好的辦法就是獲得到多個更新,之后進(jìn)行批量的更新。只更新一次頁面。
- 如果同步更新state,但是還沒有執(zhí)行render 函數(shù),那么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 并不是嚴(yán)格意義上的異步函數(shù)。他是通過隊列的延遲執(zhí)行實現(xiàn)的。使用 isBatchingUpdates
判斷當(dāng)前的setState 是加入到更新隊列還是更新頁面。當(dāng) isBatchingUpdates=ture
是加入更新隊列,否則執(zhí)行更新。
知道了React 是使用 isBatchingUpdates
來判斷是否加入更新隊列。那么為什么在 setTimeout
事件中 isBatchingUpdates
值為 false
? 原因就是在React中,對HTML的原生事件做了一次封裝叫做**合成事件。**所以在React自己的生命周期和合成事件中,可以控制 isBatchingUdates
的值,可以根據(jù)值來判斷是否更新頁面。而在宿主環(huán)境提供的原生事件中(即非合成事件),無法將 isBatchingUpdates
的值置為 false,所以就會立即執(zhí)行更新。
☆所以setState 并不是有同步的場景,而是在特殊的場景下不受React 的控制 **
總結(jié)
setState 并不是單純的同步函數(shù)或者異步函數(shù),他的同步和異步的表現(xiàn)差異體現(xiàn)在調(diào)用的場景不同。在React 的生命周期和合成事件中他表現(xiàn)為異步函數(shù)。而在DOM的原生事件等非合成事件中表現(xiàn)為同步函數(shù)。
以上就是詳解React setState數(shù)據(jù)更新機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于React setState數(shù)據(jù)更新機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React中完整實例講解Recoil狀態(tài)管理庫的使用
這篇文章主要介紹了React中Recoil狀態(tài)管理庫的使用,Recoil的產(chǎn)生源于Facebook內(nèi)部一個可視化數(shù)據(jù)分析相關(guān)的應(yīng)用,在使用React的實現(xiàn)的過程中,因為現(xiàn)有狀態(tài)管理工具不能很好的滿足應(yīng)用的需求,因此催生出了Recoil,對Recoil感興趣可以參考下文2023-05-05React學(xué)習(xí)之JSX與react事件實例分析
這篇文章主要介紹了React學(xué)習(xí)之JSX與react事件,結(jié)合實例形式分析了React中JSX表達(dá)式、屬性、嵌套與react事件相關(guān)使用技巧,需要的朋友可以參考下2020-01-01關(guān)于antd tree和父子組件之間的傳值問題(react 總結(jié))
這篇文章主要介紹了關(guān)于antd tree 和父子組件之間的傳值問題,是小編給大家總結(jié)的一些react知識點(diǎn),本文通過一個項目需求實例代碼詳解給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-06-06React onClick/onChange傳參(bind綁定)問題
這篇文章主要介紹了React onClick/onChange傳參(bind綁定)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02React-hook-form-mui基本使用教程(入門篇)
react-hook-form-mui可以幫助開發(fā)人員更輕松地構(gòu)建表單,它結(jié)合了React?Hook?Form和Material-UI組件庫,使用react-hook-form-mui,開發(fā)人員可以更快速地構(gòu)建表單,并且可以輕松地進(jìn)行表單驗證和數(shù)據(jù)處理,本文介紹React-hook-form-mui基本使用,感興趣的朋友一起看看吧2024-02-02