setState那点事

首先引用个例子react

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0
    };
  }
  
  componentDidMount() {
    this.setState({val: this.state.val + 1});
    console.log(this.state.val);

    this.setState({val: this.state.val + 1});
    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);
  }

  render() {
    return null;
  }
};
复制代码

以上代码依次输出结果是?不知道的结果的能够接下来看 知道结果的能够直接跳过 结果依次是0 0 2 3 下面剖析下setState应用场景git

第一个场景在声明周期函数中的使用

......
  state = { val: 0 }
  componentDidMount() {
    this.setState({ 
      val: this.state.val + 1
    })
    console.log(this.state.val)  
  }
 ......
复制代码

这里的输出值仍是初始值0 看下源码 关于github

function enqueueUpdate(component) {
    ......
      if (!batchingStrategy.isBatchingUpdates) {
        batchingStrategy.batchedUpdates(enqueueUpdate, component);
        return;
      }
    
      dirtyComponents.push(component);
    ......
    }
复制代码

看到有个if判断 isBatchchingUpdates true & false 自行打个断点能够看到这componentDidMount时候已经将isBatchingUpdates设置了为true 全部就会执行dirtyComponents.push(component); 不会立马更新state 这时候打印就仍是初始值 0promise

这个if判断的过程看似成了一个异步的操做bash

第二个场景在setTimeout

......
  state = { val: 0 }
  componentDidMount() {
   setTimeout( () => {
     this.setState({ 
       val: this.state.val + 1
     })
     console.log(this.state.val)
   })
  }
 ......
复制代码

这里打印的是1 断点看到这时候没有batchedUpdate调用 isBatchingUpdates仍是为false 这样batchingStrategy.batchedUpdates(enqueueUpdate, component); 这就话就当即生效了 全部这时候val 会当即更新 打印是1异步

第三个场景是批量更新

......
  state = { val: 0 }
  componentDidMount() {
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
  }
 ......
复制代码

这里结果仍是最后一个1 有一个update队列 对最后一个更新async

实现一个简易的模拟异步setState方法

var stateQueue = [] //  state队列
  function mysetState(state, componentstate) {
    
    // 写一个异步的操做
    if ( stateQueue.length === 0 ) {
        promiseasync( cleanflush );  // 清空队列
    }
    stateQueue.push({
        state,
        componentstate
    })
  }
  function cleanflush() {
    var item =setStateQueue.shift() 
    while(item) {
     const { state, componentstate } = item
     // 设置一个preState 保存以前的
     if(!componentstate.prevState) {
        componentstate.prevState = Object.assign( {}, componentstate.state);
     }
     if(typeof state === 'function') {  // 判断是否为回调
         Object.assign( componentstate.state, state( componentstate.prevState, componentstate.props ));
     } else {
        Object.assign(componentstate.state, state) // 合并state
     }
    }
  }
  function promiseasync(fn) {
    return Promise.resolve().then(function() {
      fn
    })
  }
复制代码

总结

  • setState 内部并非异步代码实现 只是函数调用栈顺讯的问题
  • 钩子函数中是异步的 setTimeout中是同步的
  • 在异步操做中对同一个值进行屡次重复setState 那将根据批量更新策略执行取最后一次的执行
  • 在同步操做中 将合并一块更新
相关文章
相关标签/搜索