深刻理解react的setState

1.組件掛載圖

  • 瞭解生命週期函數的執行順序

圖片描述

2.生命週期執行順序

嘗試一下php

  • 能夠看到在組件在組件初始化時,只執行以下三個方法:

圖片描述

  • 在父組件狀態改變時,依次執行的生命週期函數是:

圖片描述

  • 我試着分別在這幾個生命週期函數中setState了一下,發如今componentWillUpdate、render、componentDidUpdate 中會報錯,也就是說在componentWillUpdate、render、componentDidUpdate中不能夠setState。

1.那麼問題來了這些週期方法爲何不能夠setState
2.setState異步機制,怎麼處理,setState(函數)html

componentDidMount() {
    SynapseAnalytics.init({ type:Enum.pageTypeEnum.otherPage });
    this.setState({
        val:this.state.val + 1
    });
    //第一次輸出  0
    console.log(this.state.val);
    this.setState({
        val:this.state.val + 1
    });
    //第二次輸出 0
    console.log(this.state.val);
    setTimeout(()=>{
        this.setState({val:this.state.val + 1});
        //第三次輸出 2
        console.log(this.state.val);
        this.setState({
            val:this.state.val + 1
        });
        //第四次輸出 3
        console.log(this.state.val);
    }, 0);
}
  • 依次輸出0、0、二、3;由於react並非setState以後state的值就會改變的,如果這樣就太消耗內存了,失去了setState存在的意義。
  • 這裏存在一個setstate調用棧的問題,問題來了setState以後都發生了什麼
  • 1.this.setState(newState) =>
  • 2.newState存入pending隊列 =>
  • 3.調用enqueueUpdate =>
  • 4.是否處於批量更新模式 => 是的話將組件保存到dirtyComponents
  • 5.不是的話遍歷dirtyComponents,調用updateComponent,更新pending state or props
  • enqueueUpdate的源碼以下:
 function enqueueUpdate(component){
       //injected注入的
       ensureInjected();
       //若是不處於批量更新模式
       if(!batchingStrategy.isBatchingUpdates){
           batchingStrategy.batchedUpdates(enqueueUpdate, component);
           return;
       }
       //若是處於批量更新模式
       dirtyComponents.push(component);
   }
  • 若是isBatchingUpdates爲true,則對全部隊列中的更新執行batchedUpdates方法,不然只把當前組件(即調用了setState的組件)放入dirtyComponents數組中,例子中4次setState調用的表現之因此不一樣,這裏的邏輯判斷起了關鍵做用。

參考連接
參考連接react

  • 連續調用了屢次setState,可是隻引起了一次更新生命週期,由於React會將多個this.setState產生的修改放在一個隊列裏,緩一緩,攢在一塊兒,以爲差很少了在引起一次更新過程。

因此攢的過程當中若是你不停的set同一個state的值,只會觸發最後一次,例如上面那道題數組

  • 那麼問題又來了:我就想讓第三次輸出爲3,別給我覆蓋掉該怎麼辦?

其實setState還有一個用法,它不止能夠接受對象做爲參數,還能夠接受函數。異步

3.函數式的setState的用法

直接看代碼:函數

componentDidMount(){
    SynapseAnalytics.init({type:Enum.pageTypeEnum.otherPage});
    this.setState(this.fn.bind(this));
    //第一次輸出
    console.log(this.state.val);
    this.setState(this.fn.bind(this));
    //第二次輸出
    console.log(this.state.val);
    setTimeout(()=>{
        this.setState({val:this.state.val+1});
        //第三次輸出
        console.log(this.state.val);
        this.setState({
            val:this.state.val+1
        });
        //第四次輸出
        console.log(this.state.val);
    },0);
}
fn(state,props){
    return{
        val:state.val+1
    }
}
  • 這個函數能夠接受兩個參數,一個是當前state的值,一個是當前props,這個函數返回一個對象表明對state的修改。
我理解這個state其實就至關於一個全局變量,每次累加的不是this.state,而是state這個變量,因此不管累加多少次,最後將state這個變量賦值給this.state。

注意:在這累加的過程當中,若你在函數式的setState方法後面又穿插使用了傳統的對象式(this.setState({val:this.state.val + 1}))的話,以前累加的就全白費了,由於上面說過了this.state.val仍是0,它只有在render以後纔會改變。this