首先了解一個造成不能立即生效的原因,setState異步的原因是因為react的監聽事件為合成事件,state執行過程中會經歷一個生命周期函數,執行多個setState會被合並,提升性能,下面幾種方式可以避免我們的問題:
- shouleComponentUpdate
- componentWillUpdate
- render
- componentDidUpdate
setState合適同步何時異步?
由React控制的事件處理程序,以及生命周期函數調用setState不會同步更新state 。
React控制之外的事件中調用setState是同步更新的。比如原生js綁定的事件,setTimeout/setInterval等。
this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); // 執行后結果相當於 const count = this.state.count; this.setState({ count: count + 1 }); this.setState({ count: count + 1 }); this.setState({ count: count + 1 });
1、傳遞函數()=>{return state},react提供的參數props保證每次都拿到最新的
// 第一個函數調用更新state,第二個函數是更新完之后的回調。 this.setState((prevState, props) => { return { age: prevState.count + 1, };
// 獲取state console.log(this.state); // 1
});
2、設置setTimeout、setInterval,和事件玄幻有關系
setTimeout(fn,0)的含義是,指定某個任務在主線程最早可得的空閑時間執行,也就是說,盡可能早得執行。它在"任務隊列"的尾部添加一個事件,因此要等到同步任務和"任務隊列"現有的事件都處理完,才會得到執行。
setTimeout(() => { this.setState(prevState => { return { age: prevState.count + 1, }; }); // 獲取state console.log(this.state); // 1 }, 0);
3、設置js原生監聽事件addEventListener
原生事件的調用棧就比較簡單了,因為沒有走合成事件的那一大堆,直接觸發click事件,到
requestWork
,在
requestWork
里由於
expirationTime === Sync
的原因,直接走了
performSyncWork
去更新,並不像合成事件或鈎子函數中被return,所以當你在原生事件中setState后,能同步拿到更新后的state值。
handleClick3 = () => { this.setState({ count: this.state.count + 1 }); // 某些條件可能獲取不到最新的值 console.log(this.state); }; componentDidMount() { document.body.addEventListener('btn4', this.handleClick3, false); }
class Demo extends React.Component { state = { count: 0, }; handleClick0 = () => { this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); this.setState({ count: this.state.count + 1 }); // 某些條件可能獲取不到最新的值 console.log(this.state); }; // 1、把對象改為回調函數 handleClick1 = () => { this.setState(prevState => { return { age: prevState.count + 1, }; }); // 獲取state console.log(this.state); // 1 }; // 2、設置setTimeout,setInterval handleClick2 = () => { setTimeout(() => { this.setState(prevState => { return { age: prevState.count + 1, }; }); // 獲取state console.log(this.state); // 1 }, 0); }; // 3、設置原生監聽事件,不同調用react的一些生命周期,直接觸發onclick handleClick3 = () => { this.setState({ count: this.state.count + 1 }); // 某些條件可能獲取不到最新的值 console.log(this.state); }; componentDidMount() { document.body.addEventListener('btn4', this.handleClick3, false); } render() { return ( <div> <button onClick={() => this.handleClick0()}>按鈕0</button> <button onClick={() => this.handleClick1()}>按鈕1</button> <button onClick={() => this.handleClick1()}>按鈕2</button> <button id="btn4" onClick={() => this.handleClick1()}> 按鈕3 </button> </div> ); } }