1. setState基本特點
1. setState是同步執行的
setState是同步執行的,但是state並不一定會同步更新
2. setState在React生命週期和合成事件中批量覆蓋執行
在React的生命週期鉤子和合成事件中,多次執行setState,會批量執行
具體表現為,多次同步執行的setState,會進行合併,類似於Object.assign,相同的key,後面的會覆蓋前面的
當遇到多個setState呼叫時候,會提取單次傳遞setState的物件,把他們合併在一起形成一個新的
單一物件,並用這個單一的物件去做setState的事情,就像Object.assign的物件合併,後一個
key值會覆蓋前面的key值
const a = {name : 'kong', age : '17'} const b = {name : 'fang', sex : 'men'} Object.assign({}, a, b); //{name : 'fang', age : '17', sex : 'men'}
name被後面的覆蓋了,但是age和sex都起作用了
例如:
class Hello extends React.Component { constructor(){ super(); this.state = { name: 'aa' } } componentWillMount(){ this.setState({ name: 'aa' + 1 }); console.log(this.state.name); //aa this.setState({ name: 'aa' + 1 }); console.log(this.state.name); //aa } render() { return <div> <div>Hello {this.props.name}</div> <div>Hello {this.state.name}</div> </div>; } } ReactDOM.render( <Hello name="World" />, document.getElementById('container') );
componentWillMount中兩個log均為初始狀態aa,而render中的state.name則為aa2
componentWillMount中的setState均執行了,但是state的更新是延遲的,所以log出的state均為aa
而render中的state.name則在state更新之後,而且只有第二次的aa1起了作用
3. setState在原生事件,setTimeout,setInterval,Promise等非同步操作中,state會同步更新
非同步操作中setState,即使在React的鉤子或合成事件中,state都不會批量更新,而是會同步更新,
多次連續操作setState,每次都會re-render,state會同步更新
2. setState的形式
setState(object,[callback]) //物件式,object為nextState setState(function,[callback]) //函式式,function為(prevState,props) => stateChange
[callback]則為state更新之後的回撥,此時state已經完成更新,可以取到更新後的state
[callback]是在setState之後,更準確來說是當正式執行batchUpdate佇列的state更新完成後就會執行,不是在re-rendered之後
使用兩種形式的setState,state的更新都是非同步的,但是多次連續使用函式式的setState,
React本身會進行一個遞迴傳遞呼叫,將上一次函式執行後的state傳給下一個函式,因此每次執行
setState後能讀取到更新後的state值。
如果物件式和函式式的setState混合使用,則物件式的會覆蓋前面無論函式式還是物件式的任何setState,
但是不會影響後面的setState。
例如:
function increment(state,props){ return {count: state.count + 1}; } function incrementMultiple(){ this.setState(increment); this.setState(increment); this.setState({count: this.state.count + 1}); this.setState(increment); }
上面三個函式式的setState中間插入一個物件式的setState,則最後的結果是2,而不是4,
因為物件式的setState將前面的任何形式的setState覆蓋了,但是後面的setState依然起作用
3. setState的基本過程
setState的呼叫會引起React的更新生命週期的4個函式執行。
shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
當shouldComponentUpdate執行時,返回true,進行下一步,this.state沒有被更新
返回false,停止,更新this.state
當componentWillUpdate被呼叫時,this.state也沒有被更新
直到render被呼叫時候,this.state才被更新。
總之,直到下一次render函式呼叫(或者下一次shouldComponentUpdate返回false時)才能得到更新後的this.state
因此獲取更新後的狀態可以有3種方法:
1. setState函式式
2. setState在setTimeout,Promise等非同步中執行
setStatePromise(updator) { return new Promise(((resolve, reject) => { this.setState(updator, resolve); })); } componentWillMount() { this.setStatePromise(({ num }) => ({ num: num + 1, })).then(() => { console.log(this.state.num); }); }
或者
function setStateAsync(nextState){ return new Promise(resolve => { this.setState(nextState, resolve); }); } async func() { ... await this.setStateAsync({count: this.state.count + 1}); await this.setStateAsync({count: this.state.count + 1}); }
3. setState callback
setState({ index: 1 }}, function(){ console.log(this.state.index); })
4. componentDidUpdate
componentDidUpdate(){ console.log(this.state.index); }
4. setState批量更新的過程
在React的生命週期和合成事件執行前後都有相應的鉤子,分別是pre鉤子和post鉤子,pre鉤子會呼叫batchedUpdate方法將isBatchingUpdates變數置為true,開啟批量更新,而post鉤子會將isBatchingUpdates置為false
如下圖所示:
isBatchingUpdates變數置為true,則會走批量更新分支,setState的更新會被存入佇列中,待同步程式碼執行完後,再執行佇列中的state更新。
而在原生事件和非同步操作中,不會執行pre鉤子,或者生命週期的中的非同步操作之前執行了pre鉤子,但是pos鉤子也在非同步操作之前執行完了,isBatchingUpdates必定為false,也就不會進行批量更新。
5. setState的缺點
1. setState有可能迴圈呼叫
呼叫setState之後,shouldComponentUpdate、componentWillUpdate、render、componentDidUpdate 等生命週期函式會依次被呼叫(如果shouldComponentUpdate沒有返回 false的話),如果我們在render、componentWillUpdate或componentDidUpdate中呼叫了setState方法,那麼可能會造成迴圈呼叫,最終導致瀏覽器記憶體佔滿後崩潰
2、setState可能會引發不必要的渲染
可能造成不必要渲染的因素如下:
(1)新 state 和之前的一樣。這種情況可以通過 shouldComponentUpdate 解決。
(2)state 中的某些屬性和檢視沒有關係(譬如事件、timer ID等),這些屬性改變不影響檢視的顯示。
3、setState並不總能有效地管理元件中的所有狀態
因為元件中的某些屬性是和檢視沒有關係的,當元件變得複雜的時候可能會出現各種各樣的狀態需要管理,這時候用setState管理所有狀態是不可取的。state中本應該只儲存與渲染有關的狀態,而與渲染無關的狀態儘量不放在state中管理,可以直接儲存為元件例項的屬性,這樣在屬性改變的時候,不會觸發渲染,避免浪費
6. setState和replaceState的區別
setState是修改其中的部分狀態,相當於Object.assign,只是覆蓋,不會減少原來的狀態
replaceState是完全替換原來的狀態,相當於賦值,將原來的state替換為另一個物件,如果新狀態屬性減少,那麼state中就沒有這個狀態了
7. 一個例項分析
上圖的執行結果為 0 0 1 1 3 4
參考: http://www.360doc.com/content/17/0803/18/27576111_676420051.shtml
https://blog.csdn.net/kongjunchao159/article/details/72626637
https://blog.csdn.net/michellezhai/article/details/80098211
https://www.cnblogs.com/danceonbeat/p/6993674.html
https://segmentfault.com/a/1190000010682761
https://segmentfault.com/a/1190000015821018