摘要:除此之外指的是繞過通過直接添加的事件處理函數(shù)和產(chǎn)生的異步調(diào)用。但是,當在調(diào)用事件處理函數(shù)之前就會調(diào)用,這個函數(shù)會把修改為,造成的后果就是由控制的事件處理過程不會同步更新。
拋出問題
class Example extends Component { contructor () { super() this.state = { value: 0, index: 0 } } componentDidMount () { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第一次輸出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第二次輸出 setTimeout(() => { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第三次輸出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第四次輸出 }, 0); this.refs.button.addEventListener("click", this.click) } click = () => { this.setState({value: this.state.index + 1}) this.setState({value: this.state.index + 1}) } render () { return (value: {this.state.value}index: {this.props.index}) } }
這四次輸出,按常理來說分別是: 1,2,3,4。但是,實際輸出為: 0, 0, 2, 3
setState的注意點
setState不會立刻改變React組件中state的值(即setState是異步更新)
setState通過一個隊列機制實現(xiàn)state更新;
當執(zhí)行setState時,會將需要更新的state合并后放入狀態(tài)隊列,而不會立即更新,隊列可以高效的批量更新state;
通過this.state直接修改的值,state不會放入狀態(tài)隊列,當下次調(diào)用setState并對狀態(tài)隊列進行合并時,會忽略之前直接被修改的state.
setState通過引發(fā)一次組件的更新過程來引發(fā)重新繪制
此處重繪指的就是引起React的更新生命周期函數(shù)4個函數(shù):
shouldComponentUpdate(被調(diào)用時this.state沒有更新;如果返回了false,生命周期被中斷,雖然不調(diào)用之后的函數(shù)了,但是state仍然會被更新)
componentWillUpdate(被調(diào)用時this.state沒有更新)
render(被調(diào)用時this.state得到更新)
componentDidUpdate
多個相鄰的state的修改可能會合并到一起一次執(zhí)行
this.setState({name: "Pororo"}) this.setState({age: 20})
等同于
this.setState({name: "Pororo",age: 20})
上面兩塊代碼的效果是一樣的。如果每次調(diào)用都引發(fā)一次生命周期更新,那性能就會消耗很大了。所以,React會將多個this.setState產(chǎn)生的修改放進一個隊列里,等差不多的時候就會引發(fā)一次生命周期更新。
問題分析對于前兩次setState:
this.setState({value: this.state.val + 1}); console.log(this.state.value); // 第一次輸出 this.setState({value: this.state.val + 1}); console.log(this.state.value); // 第二次輸出
由于setState不會立即改變React組件中state的值,所以兩次setState中this.state.value都是同一個值0,故而,這兩次輸出都是0。因而value只被加1。
既然這樣,那么是不是可以直接操作this.state呢?比如:this.state.value=this.state.value+1;
這樣的確可以修改this.state.value的狀態(tài)但是卻不可以引發(fā)重復渲染。
所以,就必須通過React設(shè)定的setState函數(shù)去改變this.state,從而引發(fā)重新渲染。
setTimeout里面的兩次setState:
setTimeout(() => { this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第三次輸出 this.setState({value: this.state.value + 1}) console.log(this.state.value) // 第四次輸出 }, 0);
這兩次this.state的值同步更新了;
同步更新:是由React引發(fā)的事件處理(比如:onClick引發(fā)的事件處理),調(diào)用setState會異步更新this.state;
異步更新:除此之外的setState調(diào)用會同步執(zhí)行this.setState。 “除此之外”指的是:繞過React通過addEventListener直接添加的事件處理函數(shù)和setTimeout/setInterval產(chǎn)生的異步調(diào)用。
this.setState更新機制圖解:
每次setState產(chǎn)生新的state會依次被存入一個隊列,然后會根據(jù)isBathingUpdates變量判斷是直接更新this.state還是放進dirtyComponent里回頭再說。
isBatchingUpdates默認是false,也就表示setState會同步更新this.state。
但是,當React在調(diào)用事件處理函數(shù)之前就會調(diào)用batchedUpdates,這個函數(shù)會把isBatchingUpdates修改為true,造成的后果就是由React控制的事件處理過程setState不會同步更新this.state。
同步更新(函數(shù)式setState)如果this.setState的參數(shù)不是一個對象而是一個函數(shù)時,這個函數(shù)會接收到兩個參數(shù),第一個是當前的state值,第二個是當前的props,這個函數(shù)應(yīng)該返回一個對象,這個對象代表想要對this.state的更改;
換句話說,之前你想給this.setState傳遞什么對象參數(shù),在這種函數(shù)里就返回什么對象。不過,計算這個對象的方法有些改變,不再依賴于this.state,而是依賴于輸入?yún)?shù)state。
function increment(state, props) { return {count: state.count + 1}; } function incrementMultiple() { this.setState(increment); this.setState(increment); this.setState(increment); }
假如當前this.state.count的值是0,第一次調(diào)用this.setState(increment),傳給increment的state參數(shù)是0,第二調(diào)用時,state參數(shù)是1,第三次調(diào)用是,參數(shù)是2,最終incrementMultiple讓this.state.count變成了3。
對于多次調(diào)用函數(shù)式setState的情況,React會保證調(diào)用每次increment時,state都已經(jīng)合并了之前的狀態(tài)修改結(jié)果。
要注意的是,在increment函數(shù)被調(diào)用時,this.state并沒有被改變,依然,要等到render函數(shù)被重新執(zhí)行時(或者shouldComponentUpdate函數(shù)返回false之后)才被改變。同步異步setState的用法混合
function incrementMultiple() { this.setState(increment); this.setState(increment); this.setState({count: this.state.count + 1}); this.setState(increment); }
在幾個函數(shù)式setState調(diào)用中插入一個傳統(tǒng)式setState調(diào)用,最后得到的結(jié)果是讓this.state.count增加了2,而不是增加4。
這是因為React會依次合并所有setState產(chǎn)生的效果,雖然前兩個函數(shù)式setState調(diào)用產(chǎn)生的效果是count加2,但是中間出現(xiàn)一個傳統(tǒng)式setState調(diào)用,一下子強行把積攢的效果清空,用count加1取代。
所以,傳統(tǒng)式setState與函數(shù)式setState一定不要混用。
總結(jié)自:掘金(不洗碗工作室)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95186.html
摘要:除此之外指的是繞過通過直接添加的事件處理函數(shù)和產(chǎn)生的異步調(diào)用。但是,當在調(diào)用事件處理函數(shù)之前就會調(diào)用,這個函數(shù)會把修改為,造成的后果就是由控制的事件處理過程不會同步更新。 拋出問題 class Example extends Component { contructor () { super() this.state = { value: 0, ...
摘要:前端日報精選聽說你沒來總結(jié)個人使用過的移動端布局方法新特性簡介用寫組件坦然面對應(yīng)對前端疲勞中文深入理解筆記函數(shù)前端架構(gòu)經(jīng)驗分享系列教程之創(chuàng)建頁面元素龍云全棧系列教程之定位頁面元素龍云全棧第期與表單驗證技術(shù)周刊期知乎 2017-07-17 前端日報 精選 聽說你沒來 JSConf 2017?總結(jié)個人使用過的移動端布局方法 - Rni-L - SegmentFaultNode.js v8....
摘要:前端日報精選騰訊前端團隊社區(qū)源碼分析入門指南一些關(guān)于使用的心得基本類型與引用類型知多少掘金中文第期框架選型周刊第期入門系列模塊車棧重構(gòu)基于的網(wǎng)絡(luò)請求庫某熊的全棧之路的那些奇技淫巧的平凡之路模仿寫個數(shù)組監(jiān)聽掘 2017-07-01 前端日報 精選 Why you shouldn`t use Preact, Fast-React, etc. to replace React today -...
摘要:中的的線程是以事件循環(huán)和消息隊列的形式存在,包含兩個任務(wù)隊列,一個是內(nèi)部隊列,一個是外部隊列,而的優(yōu)先級又高于。同時還有處理按住時的事件額外處理,同時手勢處理一般在的子類進行。谷歌大會之后,有不少人咨詢了我 Flutter 相關(guān)的問題,其中有不少是和面試相關(guān)的,如今一些招聘上也開始羅列 Flutter 相關(guān)要求,最后想了想還是寫一期總結(jié)吧,也算是 Flutter 的階段復習。 ??系統(tǒng)完...
閱讀 831·2021-10-14 09:43
閱讀 2148·2021-09-30 09:48
閱讀 3484·2021-09-08 09:45
閱讀 1124·2021-09-02 15:41
閱讀 1919·2021-08-26 14:15
閱讀 810·2021-08-03 14:04
閱讀 3006·2019-08-30 15:56
閱讀 3102·2019-08-30 15:52