在react中,setState是用以改變class組件狀態的函數,它有兩種用法:
一 傳入一個updater函數,該函數有兩個參數,一個是當前的state,還有一個是當前的props。該函數的返回值須要是一個更改的state值的對象,它將於state進行淺合併,其用法以下:html
this.setState((state, props) => { return { count: state.count + props.number }; });
二 直接傳入一個對象:react
this.setState({ count: this.state.count + this.props.number });
setState函數還能夠接受第二個參數,該參數爲一個函數,將在更改的State生效以後調用:git
console.log(this.state.count); // 1 this.setState({ count: 0 }, () => { console.log(this.state.count); // 0 }); console.log(this.state.count); // ? 此處便可能是1,也多是0
從上面代碼能夠看到,最後一行輸出的count是不固定的,這是爲何呢?
由於在react中,class內的事件處理程序會默認進行批處理,即若是你在componentDidMount裏面調用三次setState函數,那麼它最終會在componentDidMount執行完畢後,將三個State的更改合併爲一次調用。因此這時候setState就是異步的。
而在其餘場景下,setState將會是同步的,例如setTimeout內, Promise的then裏面。
一個簡單的例子:github
class SetStateExample extends Component { constructor() { super(); this.state = { count: 0 }; this.onClick = this.onClick.bind(this); } componentDidMount() { console.log('componentDidMount before', this.state.count); this.setState({ count: this.state.count + 1 }); console.log('componentDidMount after', this.state.count); } onClick() { console.log('onClick before', this.state.count) this.setState({ count: this.state.count + 1 }, () => { console.log('setState callback', this.state.count); }); console.log('onClick after', this.state.count); Promise.resolve().then(() => { console.log('promise.then before', this.state.count); this.setState({ count: this.state.count + 1 }); console.log('promise.then after', this.state.count); this.onClassEvent(); }); } onClassEvent() { console.log('onClassEvent before', this.state.count); this.setState({ count: this.state.count + 1 }); console.log('onClassEvent after', this.state.count); } render() { return <div className="test"> <div>count: {this.state.count}</div> <button onClick={this.onClick}>點擊改變count</button> </div>; } }
讓咱們運行結果:promise
首先第一第二行輸出是在componentDidMount裏面,咱們在函數內調用了setState,並在先後分別輸出了改變的值,結果代表,函數調用前與函數調用後該值並無當即改變,則代表在這裏setState是一個異步調用。那麼初步斷定在生命週期函數內部,setState是異步的調用。異步
而後第三第四行輸出是在onClick函數的回調裏面,該函數定義在class中,經過用戶點擊觸發。在setState調用先後咱們的輸出結果是一致的,這也代表其是一個異步調用。而在setState第二個參數中咱們輸出了改變後的count, 即第五行輸出,代表咱們的更改生效了。函數
而後第六行之後的輸出是咱們在onClick函數內調用了promise.resolve().then()輸出的,它是一個異步調用,react是沒法知道它何時執行,何時完成執行的,因此這時候react默認setState是同步的。從輸出咱們能夠看到每次更改以後,state的值都是當即變化生效的。性能
而在promise的回調內,咱們還調用了一個定義於class內的事件函數,可是該事件函數內的setState也是同步的形式。這說明了setState的同步或者異步與其定義位置並無直接的關係,而應該取決因而否由React直接進行調用,由於只有是React直接調用的狀況下,它才知道該函數何時執行完畢,才能進行批處理的優化。不然則默認是同步的調用。(具體內部實現就不展開了,由於我也不是特別懂HHHH,反正意思就大概是這麼個意思)優化
因此當在一些回調內部調用setState時應該注意將多個setState合併,由於它是同步的,屢次更新狀態會很影響性能。
以及須要注意進行異步調用的時候,若是須要使用變化後的值,請確保在異步調用完成後,通常是在setState的回調內,或者在componentDidUpdate鉤子內,可是請注意當心使用,由於很容易一不當心致使循環調用而崩潰。this
若是你想在本應同步調用的回調內,對setState進行異步調用,即讓它進行批處理,React也提供了一個API:
promise.then(() => { // Forces batching ReactDOM.unstable_batchedUpdates(() => { this.setState({a: true}); // Doesn't re-render yet this.setState({b: true}); // Doesn't re-render yet this.props.setParentState(); // Doesn't re-render yet }); // When we exit unstable_batchedUpdates, re-renders once });
在unstable_batchedUpdates內部進行的setState會是異步調用,可是該API是不穩定的,由於後續的React版本更新中將會默認進行批處理即異步調用,屆時該API將被刪除。而這個後續的版本,極可能就是React 17
記錄與分享,歡迎斧正,虛心求教
參考鏈接:
https://stackoverflow.com/que...
https://react.docschina.org/d...
https://github.com/Advanced-F...
https://github.com/sisterAn/b...