關於React中setState同步或異步問題的理解
1. setState同步?異步?
在 React 的類式組件中,我們可以使用setState方法更新state狀態。但有些時候使用setState之後,得不到最新的數據。
其實 React 中setState本身執行的過程和代碼是同步的,隻是因為 React 框架本身的性能優化機制而導致的。React 中合成事件和生命周期函數的調用順序在更新之前,導致在合成事件和生命周期函數中無法立刻得到更新後的值,形成瞭異步的形式。
假如在一個合成事件中,循環調用瞭setState方法n次,如果 React 沒有優化,當前組件就要被渲染n次,這對性能來說是很大的浪費。所以,React 為瞭性能原因,對調用多次setState方法合並為一個來執行。當執行setState的時候,state中的數據並不會馬上更新。
前面已經說到,在 React 的合成事件和生命周期函數中直接調用setState,會表現出異步的形式。
除此之外,如果越過 React 的性能優化機制,在原生事件、setTimeout中使用setState,就會表現出同步的形式。
2. 表現為異步
1. React 合成事件
在 React 中直接使用的事件,如onChange、onClick等,都是由 React 封裝後的事件,是合成事件,由 React 管理。那麼由於性能優化的機制,在合成事件中直接調用setState,將表現出異步的形式。
如下代碼,在合成事件onClick中,直接將state中的count加1,並在此之後打印count的值,結果第一次點擊按鈕時,會打印出0,而不是最新的1。
state = { count: 0 }; add = () => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 0 }; render() { return ( <> <div>當前計數:{this.state.count}</div> <button onClick={this.add}>add</button> </> ); }
2. 生命周期函數
生命周期函數也是由 React 所管理,在生命周期函數中直接調用setState,也會表現出異步的形式。
如下代碼,在生命周期componentDidMount函數中,將state中的count加1,並在此之後打印count的值,結果打印出0,而不是最新的1。
state = { count: 0 }; componentDidMount() { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 0 } render() { return ( <> <div>當前計數:{this.state.count}</div> <button>add</button> </> ); }
3. 表現為同步
1. 原生事件
setState本身執行的過程是同步的,使用原生事件,繞過 React 的管理,將表現出同步的形式。
如下代碼,通過id獲取到 DOM 元素,用原生方法綁定點擊事件。在點擊事件中,將state中的count加1,並在此之後打印count的值,結果會打印最新的count值1。
state = { count: 0 }; componentDidMount() { const btn = document.getElementById('btn'); btn.onclick = () => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 1 }; } render() { return ( <> <div>當前計數:{this.state.count}</div> <button id="btn">add</button> </> ); }
2. setTimeout
如下代碼,在生命周期componentDidMount函數中寫瞭一個定時器setTimeout,在setTimeout內部將state中的count加1,並在此之後打印count的值,結果會打印最新的count值1。
setState雖然也是寫在生命周期componentDidMount函數中的,但並不是直接寫在componentDidMount裡,而是套瞭一層setTimeout。這樣,setState就表現出同步的形式。
state = { count: 0 }; componentDidMount() { setTimeout(() => { this.setState({ count: this.state.count + 1 }); console.log(this.state.count); // 1 }, 0); } render() { return ( <> <div>當前計數:{this.state.count}</div> <button>add</button> </> ); }
4. setState的第二個參數
無論setState的對象式寫法,還是函數式寫法,都有第二個參數,為可選的回調函數,這個回調函數在狀態更新完畢、界面也更新後(render調用後)才被調用。
如下代碼所示,setState雖然直接在componentDidMount中調用,但在setState的回調函數中打印count的值,得到瞭最新的值1,因為回調函數在狀態更新完畢後才被調用,當然能得到最新的count瞭。
state = { count: 0 }; componentDidMount() { this.setState({ count: this.state.count + 1 }, () => { console.log(this.state.count); // 1 }); } render() { return ( <> <div>當前計數:{this.state.count}</div> <button>add</button> </> ); }
到此這篇關於關於React中setState同步或異步問題的理解的文章就介紹到這瞭,更多相關React中setState同步或異步內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- React setState是異步還是同步原理解析
- 淺談React多個setState會調用幾次
- react新版本生命周期鉤子函數及用法詳解
- 代碼解析React中setState同步和異步問題
- React 原理詳解