轉自:https://www.cnblogs.com/mengff/p/9611614.html
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