React
控制的事件處理程序,以及生命週期內調用setState
是異步更新state
React
控制以外的事件中調用setState
是同步更新state
,好比原生js綁定事件、setTimeout
/setInrerval
等。setState
的「異步」並非說內部由異步代碼實現,自己的執行過程和代碼都是同步的。算法
之因此會有一種異步方法的表現形式,歸根結底仍是由於React
框架自己的性能機制所致使的。由於每次調用setState
都會觸發更新,異步操做是爲了提升性能,將多個狀態合併一塊兒更新,減小re-render
調用。性能優化
咱們來看一些例子:框架
state = { number: 1 } componentDidMount() { this.setState({ number: 3 }); console.log(this.state.number); // 1 }
因而可知該事件處理程序中的setState
是異步更新state
的。異步
state = { number: 1 } componentDidMount() { setTimeout(() => { this.setState({ number: 3 }); }, 0); console.log(this.state.number); // 3 }
上面咱們講到,setState
自己並非一個異步方法,之因此會變現出一種異步的形式,是由於React
框架自己的一種性能優化機制。那麼基於這一點,假如咱們能繞過React
的機制,就能夠令setState
以同步的形式體現。函數
state = { number: 1 } componentDidMount() { document.body.addEventListener('click', this.resetState, false); } resetState() { this.setState({ number: 3 }); console.log(this.state.number); // 3 }
一樣的,原生事件也能夠繞過React
的性能優化機制,達到同步更新的表現。性能
在React
的setState
函數實現中,會根據一個變量isBatchingUpdates
判斷是否直接更新this.state
,仍是放入隊列中延時更新。優化
而isBatchingUpdates
默認是false
,標識setState
是同步更新this.state
。可是有一個函數batchedUpdates
會把isBatchingUpdates
修改成true
,而當React
在調用事件處理函數以前就會先調用這個函數將isBatchingUpdates
修改成true
。這樣由React
控制的事件處理過程setState
就不會同步更新this.state
。this
function enqueueUpdate(component){ //injected注入的 ensureInjected(); //若是不處於批量更新模式 if(!batchingStrategy.isBatchingUpdates){ batchingStrategy.batchedUpdates(enqueueUpdate, component); return; } //若是處於批量更新模式 dirtyComponents.push(component); }
事實上setState
內部的執行過程是很複雜的,大體過程包括更新state
,建立新的VNode
,再通過diff
算法對比差別,決定渲染哪一部分以及怎麼渲染,最終造成最新的UI。這一過程包含組件的四個生命週期函數:spa
shouComponentUpdate
componentWillUpdate
render
componentDidUpdate
若是是子組件而且依賴父組件,還會執行一個鉤子函數componentWillReceiveProps
。code
假如setState
是同步更新的,每次更新這個過程都要完整執行一次,無疑會形成性能問題。事實上這些生命週期爲純函數,對性能還好,可是diff比較、更新DOM總消耗時間和性能吧。
在「異步」中若是對同一個值進行屢次setState
, setState
的批量更新策略會對其進行覆蓋,取最後一次的執行。
state = { number: 1 } handleClick() { const number = this.state.number; this.setState({ number: number + 1 }); this.setState({ number: number + 1 }); this.setState({ number: number + 1 }); }
最終的結果只加了1。
若是是同時 setState
多個不一樣的值,在更新時會對其進行合併批量更新。
hanldeClick() { this.setState({ name: 'Clearlove' }); this.setState({ age: 18 }); }
在hanldeClick
處理程序中調用了兩次setState
,可是render
只執行了一次。由於React
會將多個this.setState
產生的修改放在一個隊列裏進行批延時處理。
setState
提供了一個回調函數供開發者使用,在回調函數中,咱們能夠實時的獲取到更新以後的數據。仍是以剛纔的例子作示範:
state = { number: 1 } componentDidMount() { this.setState({ number: 3 }, () => { console.log(this.state.number); // 3 }); }