React【 SetState Principle 】

439 阅读2分钟

Intro

setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. setState方法将组件状态的改变添加到队列中,同时通知React重新渲染该组件及其子组件。 React does not guarantee that the state changes are applied immediately. React(在同调用场景中)无法确保状态是立即更新的。

features特点

  1. 批量更新: batchUpdate,调用多个setState会进行合并,放在一个队列中先保存起来,再一次执行
  2. 状态合并:只能曾/改,不能删减
  3. 异步执行:立刻执行,eg: setTiemout
  • 如果不通过setState改变的状态,不会引起组件的更新
 state = { numer: 0 };
 mergeAdd = () => {
    this.setState({ number: this.state.number + 1 });
    this.setState({ number: this.state.number + 1 });
    this.setState({ number: this.state.number + 1 });
    console.log(this.state.number);  
    // 打印0
    // 三次调用的setState会放在队列中执行,且只有最后一个触发了state改变
    // 此时 batchUpdate = true
  }
  
  asyncAdd = () => {
      setTimeout(() => {
          this.setState({ number: 1 });
          this.setState({ number: 2 });
          this.setState({ number: 3 });
          console.log(this.state.number);
          // 打印6
          // 异步调用的时候,三个setState会依次立即触发,并不会放在队列中,延时触发
          // 此时batchUpdate = false
      })
  }

调用方式

  1. 传入要改变的状态对象
this.setState({ number: this.state.stateProp + 1 })
  1. 传入两个回调函数,拿到上一次改变后的状态
this.setState( prevState => ({ stateProp: prevState.stateProp }), () => {/* to something... */} )

实现原理

class Component{
    state = { number: 1, name: 'stella' };
    updateQueue = [];
    callbackQueue = [];
    batchUpdate = false;
    
    setState(partialState, callback) {
        if (this.batchUpdate) { // 批量更新
            this.updateQueue.push(partialState);
            callback && this.callbackQueue.push(callback);
        } else { // 异步更新状态时 立即更新
            this.state = typeof partialState === 'function' ? 
                partialState(this.state) :  
                partialState;
        }
    }
    
    flushUpdate() {
        let state = this.state,;
        for (let i = 0; i < this.updateQueue; i++){
            let updateState = this.updateQueue[i];
            let partialState = typeof updateState === 'function' ? 
                updateState(state) : 
                updateState;
            state = { ...state, ...partialState }; // 状态合并
            // 注意updateState()调用传参是当前state, 不能是this.state,
            // 实现每一次调用setState都能拿到上一次传入的值
        }
        this.state = state;
        this.callbackQueue.forEach(callback => callback());
        this.batchUpdate = false; // 关闭批量更新模式
    }
    
    add() {
        this.batchUpdate = true; // 开启批量更新模式
        this.setState({ number: this.state.number + 1 });
        this.setState(
            prevState => ({ number: prevState.number + 1 }), 
            () => console.log(this.state)
        );
        this.flushUpdate();
    }
}