setState同步OR異步

setState()更新狀態的兩種寫法

  1. setState(updater,[callback])react

    updater 爲返回stateChange的函數的對象,(state,props) => stateChange面試

    接收的state和props被保證爲最新的promise

    函數方式:babel

    1、若是新狀態依賴於原狀態
    this.setState((state,props) => ({
       count: state.count+1 
    }),()=>{
        // callback
    })
    複製代碼
  2. setState(stateChange,[callback])dom

stateChange爲對象異步

callback是可選的回調函數,在狀態更新且界面更新後才執行函數

對象方式:測試

1、新狀態不依賴於原狀態 ==>使用對象方式
this.setState({count:this.state.count+1},()=>{
    // callback
})
複製代碼

案例

<body>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  <div id="root"></div>
  <script type="text/babel">
      
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count:0
        }
      }
      test1 = ()=> {
        // 若是新狀態依賴於原狀態,使用函數方式
        this.setState((state,props)=> ({
          count: state.count+1
        }))
        console.log('test1以後獲取count的值:',this.state.count); // 0
      }
      test2 = ()=> {
        // 新狀態不依賴於原狀態 ==>使用對象方式
        const count = this.state.count + 1;
        this.setState({count});
        console.log('test2以後獲取count的值:',this.state.count);
      }
      test3 = ()=> {
        this.setState(state =>({
          count: state.count + 1
        }),()=>{
          console.log('test3-----setState回調函數中count的值:',this.state.count);
        })
      }
      render() {
        return (
          <div>
            <h1>A組件:{this.state.count}</h1>
            <button onClick={this.test1}>測試1</button><br />
            <button onClick={this.test2}>測試2</button><br />  
            <button onClick={this.test3}>測試3</button><br />                
          </div>
        )
      }
    }
    ReactDOM.render(<App />,document.getElementById('root'));
  </script>

</body>
複製代碼

setState的同步與異步

setState()內部是利用的 「事務」 實現異步更新狀態ui

同步

定時器,DOM事件監聽回調,Promisethis

異步

在react控制的回調函數中:生命週期鉤子 、 react事件監聽的回調

異步的setState() 屢次調用,如何處理?

注意:異步狀況

  • setState({}): 合併更新一次狀態,只調用一次render()更新界面 --- 狀態更新和界面更新都合併了
  • setState(fn): 更新屢次狀態,但只調用一次render更新界面, ----狀態更新沒有合併但界面更新合併了
  • 先執行setState({}) 對象方式再執行**setState(fn)**函數方式,狀態更新沒有合併,但界面更新合併了

獲得異步更新後的狀態數據

在setState()的callback回調函數中

案例

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  <div id="root"></div>
  <script type="text/babel">
    
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count:0
        }
      }
      /** * react事件監聽相關的回調中:- setState()是異步更新狀態的 */ 
      test1 = ()=> {
        console.log('setSate回調函數以前---react事件監聽回調',this.state.count);        
        this.setState(state=>({
          count:state.count+1
        }))  
        console.log('setSate回調函數以後---react事件監聽回調',this.state.count);
      }
      /** * react生命週期鉤子中:- setState()是異步更新狀態的 */ 
      componentDidMount() {
        console.log('setSate回調函數以前---生命週期鉤子',this.state.count);        
        this.setState(state=>({
          count:state.count+1
        }))  
        console.log('setSate回調函數以後--生命週期鉤子',this.state.count); 
      }
      // 同步 (定時器,DOM事件監聽回調,Promise)

      test2 = ()=> {
        setTimeout(() => {
          console.log('setSate回調函數以前---定時器',this.state.count);        
          this.setState(state=>({
            count:state.count+1
          }))  
          console.log('setSate回調函數以後---定時器',this.state.count); 
        }, 0);
      }
      test4 = () => {
        const btn4 = this.refs.btn4;
        btn4.onclick = ()=> {
          console.log('setSate回調函數以前---原生DOM',this.state.count);        
          this.setState(state=>({
            count:state.count+1
          }))  
          console.log('setSate回調函數以後---原生DOM',this.state.count); 
        }
      }
      test3 = ()=> {
        Promise.resolve().then(res => {
          console.log('setSate回調函數以前---Promise',this.state.count);        
          this.setState(state=>({
            count:state.count+1
          }))  
          console.log('setSate回調函數以後---Promise',this.state.count); 
        })
      }
      test5 = ()=> {
        console.log('setState 函數方式 - 1 --調用前',this.state.count);
        this.setState(state => ({
          count: state.count+1
        }))
        console.log('setState 函數方式 - 1 --調用後',this.state.count);
        this.setState(state => ({
          count: state.count+1
        }))
        console.log('setState 函數方式 - 2 --調用後',this.state.count);
      }
      test6 = ()=> {
        console.log('setState 對象方式 - 1 --調用前',this.state.count);
        this.setState({count:this.state.count+1});
        console.log('setState 對象方式 - 1 --調用後',this.state.count);
        this.setState({count:this.state.count+1});
        console.log('setState 對象方式 - 2 --調用後',this.state.count);
      }
      test7 = () => {
        console.log('setState 對象方式 - 1 --調用前',this.state.count);
        this.setState({count:this.state.count+1});
        console.log('setState 對象方式 - 1 --調用後',this.state.count);
        this.setState(state => ({
          count: state+1
        }));
        console.log('setState 函數方式 - 1 --調用後',this.state.count);
      }

      render() {
        return (
          <div> <h1>A組件:{this.state.count}</h1> <button onClick={this.test1}>測試1</button><br /> <button onClick={this.test2}>測試2</button><br /> <button onClick={this.test3}>Promise</button><br /> <button ref="btn4" onClick={this.test4}>原生DOM</button><br /> <button onClick={this.test5}>setState屢次調用---函數方式</button><br /> <button onClick={this.test6}>setState屢次調用---對象方式</button><br /> <button onClick={this.test7}>setState屢次調用---對象方式和函數方式</button><br /> </div>
        )
      }
    }
    ReactDOM.render(<App />,document.getElementById('root')); </script>
複製代碼

setState()常見的面試題

若是上面講解的內容都能理解的,下面的內容很容易的。🙂

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  <div id="root"></div>
  <script type="text/babel">
    class App extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          count:0
        }
      }
      /** * Promise和定時器相比,promise會先執行 * setState()執行一次,致使數據發生變化,就會觸發render的更新 */ 

      componentDidMount() {
        this.setState({count:this.state.count+1});
        this.setState({count:this.state.count+1});
        console.log('1----',this.state.count); // 2 --> 0(上面兩個會被合併成一個)

        this.setState(state => ({
          count: state.count+1
        }))
        this.setState(state => ({
          count: state.count+1
        }))
        console.log('2-----',this.state.count); // 3 --> 0 (第四次執行,輸出結果爲3)
        
        setTimeout(() => {
          this.setState({count:this.state.count+1});
          console.log('timeout-1',this.state.count); // 8 ---> 6
          this.setState({count:this.state.count+1});
          console.log('timeout-2',this.state.count); // 10 ---> 7
        }, 0);

        Promise.resolve().then(value=>{
          this.setState({count:this.state.count+1});
          console.log('promise-1',this.state.count); // 5 --> 4
          this.setState({count:this.state.count+1});
          console.log('promise-2',this.state.count); // 7 --> 5
        })

      }
     
      render() {
        const count = this.state.count;
        console.log('render',count); // 1 --- 0 , 4 ---> 3, 6 ---> 4 , 9 ---> 6,11 --> 7
        return (
          <div> <h1>A組件:{this.state.count}</h1> </div>
        )
      }
    }
    ReactDOM.render(<App />,document.getElementById('root')); </script>
複製代碼
相關文章
相關標籤/搜索