react中setState方法到底是非同步還是同步,其實這個是分在什麼條件下是非同步或者同步。 1.先來回顧一下react組件中改變state的幾種方式: import React, { Component } from 'react' class Index extends Component { ...
react中setState方法到底是非同步還是同步,其實這個是分在什麼條件下是非同步或者同步。
1.先來回顧一下react組件中改變state的幾種方式:
import React, { Component } from 'react' class Index extends Component { state={ count:1 } test1 = () => { // 通過回調函數的形式 this.setState((state,props)=>({ count:state.count+1 })); console.log('test1 setState()之後',this.state.count); } test2 = () => { // 通過對象的方式(註意:此方法多次設置會合併且只調用一次!) this.setState({ count:this.state.count+1 }); console.log('test2 setState()之後',this.state.count); } test3 = () => { // 不能直接修改state的值,此方法強烈不建議!!!因為不會觸發重新render this.state.count += 1; } test4 = () => { // 在第二個callback拿到更新後的state this.setState({ count:this.state.count+1 },()=>{// 在狀態更新且頁面更新(render)後執行 console.log('test4 setState()之後',this.state.count); }); } render() { console.log('render'); return ( <div> <h1>currentState:{this.state.count}</h1> <button onClick={this.test1}>測試1</button> <button onClick={this.test2}>測試2</button> <button onClick={this.test3} style={{color:'red'}}>測試3</button> <button onClick={this.test4}>測試4</button> </div> ) } } export default Index;
2.setState()更新狀態是非同步還是同步:
需要判斷執行setState的位置
同步:在react控制的回調函數中:生命周期鉤子/react事件監聽回調
import React, { Component } from 'react' class Index extends Component { state={ count:1 } /* react事件監聽回調中,setState()是非同步狀態 */ update1 = () => { console.log('update1 setState()之前',this.state.count); this.setState((state,props)=>({ count:state.count+1 })); console.log('update1 setState()之後',this.state.count); } /* react生命周期鉤子中,setState()是非同步更新狀態 */ componentDidMount() { console.log('componentDidMount setState()之前',this.state.count); this.setState((state,props)=>({ count:state.count+1 })); console.log('componentDidMount setState()之後',this.state.count); } render() { console.log('render'); return ( <div> <h1>currentState:{this.state.count}</h1> <button onClick={this.update1}>測試1</button> <button onClick={this.update2}>測試2</button> </div> ) } } export default Index;
非同步:非react控制的非同步回調函數中:定時器回調/原生事件監聽回調/Promise
import React, { Component } from 'react' class Index extends Component { state={ count:1 } /* 定時器回調 */ update1 = () => { setTimeout(()=>{ console.log('setTimeout setState()之前',this.state.count);//1 this.setState((state,props)=>({ count:state.count+1 })); console.log('setTimeout setState()之後',this.state.count);//2 }); } /* 原生事件回調 */ update2 = () => { const h1 = this.refs.count; h1.onclick = () => { console.log('onClick setState()之前',this.state.count);//1 this.setState((state,props)=>({ count:state.count+1 })); console.log('onClick setState()之後',this.state.count);//2 } } /* Promise回調 */ update3 = () => { Promise.resolve().then(value=>{ console.log('Promise setState()之前',this.state.count);//1 this.setState((state,props)=>({ count:state.count+1 })); console.log('Promise setState()之後',this.state.count);//2 }); } render() { console.log('render'); return ( <div> <h1 ref='count'>currentState:{this.state.count}</h1> <button onClick={this.update1}>測試1</button> <button onClick={this.update2}>測試2</button> <button onClick={this.update3}>測試3</button> </div> ) } } export default Index;
3.setState()多次調用的問題:
非同步的setState()
(1)多次調用,處理方法:
setState({}):合併更新一次狀態,只調用一次render()更新界面,多次調用會合併為一個,後面的值會覆蓋前面的值。
setState(fn):更新多次狀態,只調用一次render()更新界面,多次調用不會合併為一個,後面的值會覆蓋前面的值。
import React, { Component } from 'react' class Index extends Component { state={ count:1 } update1 = () => { console.log('update1 setState()之前',this.state.count); this.setState((state,props)=>({ count:state.count+1 })); console.log('update1 setState()之後',this.state.count); console.log('update1 setState()之前2',this.state.count); this.setState((state,props)=>({ count:state.count+1 })); console.log('update1 setState()之後2',this.state.count); } update2 = () => { console.log('update2 setState()之前',this.state.count); this.setState({ count:this.state.count+1 }); console.log('update2 setState()之後',this.state.count); console.log('update2 setState()之前2',this.state.count); this.setState({ count:this.state.count+1 }); console.log('update2 setState()之後2',this.state.count); } update3 = () => { console.log('update3 setState()之前',this.state.count); this.setState({ count:this.state.count+1 }); console.log('update3 setState()之後',this.state.count); console.log('update3 setState()之前2',this.state.count); this.setState((state,props)=>({ count:state.count+1 }));// 這裡需要註意setState傳參為函數模式時,state會確保拿到的是最新的值 console.log('update3 setState()之後2',this.state.count); } update4 = () => { console.log('update4 setState()之前',this.state.count); this.setState((state,props)=>({ count:state.count+1 })); console.log('update4 setState()之後',this.state.count); console.log('update4 setState()之前2',this.state.count); this.setState({ count:this.state.count+1 });// 這裡需要註意的是如果setState傳參為對象且在最後,那麼會與之前的setState合併 console.log('update4 setState()之後2',this.state.count); } render() { console.log('render'); return ( <div> <h1>currentState:{this.state.count}</h1> <button onClick={this.update1}>測試1</button> <button onClick={this.update2}>測試2</button> <button onClick={this.update3}>測試3</button> <button onClick={this.update4}>測試4</button> </div> ) } } export default Index;
(2)如何得到setState非同步更新後的狀態數據:
在setState()的callback回調函數中
4.react中常見的setState面試題(setState執行順序)
import React, { Component } from 'react' // setState執行順序 class Index extends Component { state={ count:0 } componentDidMount() { this.setState({count:this.state.count+1}); this.setState({count:this.state.count+1}); console.log(this.state.count);// 2 => 0 this.setState(state=>({count:state.count+1})); this.setState(state=>({count:state.count+1})); console.log(this.state.count);// 3 => 0 setTimeout(() => { this.setState({count:this.state.count+1}); console.log('setTimeout',this.state.count);// 10 => 6 this.setState({count:this.state.count+1}); console.log('setTimeout',this.state.count);// 12 => 7 }); Promise.resolve().then(value=>{ this.setState({count:this.state.count+1}); console.log('Promise',this.state.count);// 6 => 4 this.setState({count:this.state.count+1}); console.log('Promise',this.state.count);// 8 => 5 }); } render() { console.log('render',this.state.count);// 1 => 0 // 4 => 3 // 5 => 4 // 7 => 5 // 9 => 6 // 11 => 7 return ( <div> <h1>currentState:{this.state.count}</h1> <button onClick={this.update1}>測試1</button> <button onClick={this.update2}>測試2</button> <button onClick={this.update3}>測試3</button> <button onClick={this.update4}>測試4</button> </div> ) } } export default Index;
總結:react中setState()更新狀態的2種寫法
1)setState(updater,[callback])
updater:為返回stateChange對象的函數:(state,props)=>stateChange,接收的state和props都保證為最新的
2)setState(stateChange,[callback])
stateChange為對象,callback是可選的回調函數,在狀態更新且界面更新後才執行
註意:
對象是函數方式的簡寫方式
如果新狀態不依賴於原狀態,則使用對象方式;
如果新狀態依賴於原狀態,則使用函數方式;
如果需要在setState()後獲取最新的狀態數據,在第二個callback函數中獲取