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