React细节知识之batchStrategy引发的问题

      官方文档如下解释 setState:

Sets a subset of the state. Always use this to mutate state. You should treat this.state as immutable.
There is no guarantee that this.state will be immediately updated, so accessing this.state after calling this method may return the old value.
There is no guarantee that calls to setState will run synchronously,as they may eventually be batched together. You can provide an optional callback that will be executed when the call to setState is actually completed.

      this.state是不可变的,当设置state的子集时必须要使用setState。
      setState并不能确保this.state会立即更新,在调用这个函数(setState)后可能会返回没被更新的值。
      setState也并不能保证被调用后会同步运行,因为它们最终可能会被批量处理。你可以提供一个可选择的回调函数,在setState处理完成后将被立即执行。

      针对第二段this.state不会立即更新产生的问题有下面这个实例:
场景:在onclick的回调函数中对state进行量测更新,那么量测更新的结果都是基于更新之前的旧值进行计算的。

var LikeButton = React.createClass({
        getInitialState: function() {
          return {value: 0};
        },
        handleClick: function(event) {
          this.setState({value: this.state.value + 1});
          this.setState({value: this.state.value + 2});
        },
        render: function() {
          var text = '当前值为'+this.state.value;
          return (
          <div>
            <button onClick={this.handleClick}> 点击+2
            </button>
            <p>{text}</p>
            </div>
          );
        }
      });

      ReactDOM.render(
        <LikeButton />,
        document.getElementById('example')
      );

      我们期望的结果是每点击button一次,value的值加3,但是分别在同一个回调函数的两个setState中实现的。可是实现的结果并不是我们想要的,每次value的值都是+2。那么为什么会这样呢?
      在setState是批量处理策略来进行更新的,那么更新组件updateComponent方法主要是计算prop,钩子函数,还有计算新的state。新的state是通过_processPendingState方法实现的,在ReactCompositeComponent.js找到其代码:

_processPendingState: function (props, context) {
    var inst = this._instance;
    var queue = this._pendingStateQueue;
    var replace = this._pendingReplaceState;
    this._pendingReplaceState = false;
    this._pendingStateQueue = null;
    if (!queue) {
      return inst.state;
    }

    if (replace && queue.length === 1) {
      return queue[0];
    }

    var nextState = _assign({}, replace ? queue[0] : inst.state);
    for (var i = replace ? 1 : 0; i < queue.length; i++) {
      var partial = queue[i];
      _assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);
    }

    return nextState;
  }

      其中queue是批量处理中待处理的state队列,nextState的第一个元素是state的原始值,后面的元素是通过遍历queue添加的,而且对于相同的state,他们都是基于同一个原始值进行更新。因此,我们在setStete中两次更新value的值都是基于上次更新或初始化的结果,由于+2是在队列的后面,因此渲染出来的结果是0,2,4,…那么如何才能实现我们想要的效果呢?先来看一看这对nextState进行复制的这段代码:

_assign(nextState, typeof partial === 'function' ? partial.call(inst, nextState, props, context) : partial);

      如果stateQueue内部的元素是函数的话,则调用该函数进行渲染,而且他接受的是nextState,也就是最新更新的state,从而可以实现我们想要的效果。因此,该实例的handleClick可以更改为:

  handleClick(){
          this.setState(()=>{value: this.state.value + 1});
          this.setState(()=>{value: this.state.value + 2});
        }

      此时,每次点击按钮会显示的值是0,3,6,9…
      还有其他方法可以实现这个效果嘛?我在这篇文章中找到了答案https://juejin.im/post/599b8f066fb9a0247637d61b,他采用超时调用的方法实现的:

handleClick() { 
setTimeout(()=>{ 
this.setState({ value: this.state.value + 1 }); 
this.setState({ value: this.state.value + 2 });
 },0)
}

      通过setTimeout函数的包装,两次setState都会在click触发的批量更新batchedUpdates结束之后执行,这两次setState会触发两次批量更新batchedUpdates,当然也会执行两个事务以及函数flushBatchedUpdates,这就相当于一个同步更新的过程,自然可以达到我们的目的,这也就解释了为什么React文档中既没有说setState是同步更新或者是异步更新,只是模糊地说到,setState并不保证同步更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 使用 create-react-app 快速构建 React 开发环境 项目的目录结构如下: React JSX ...
    majun00阅读 3,477评论 0 0
  • 个人笔记, 转载请注明转载自 szhshp的第三边境研究所 Refs and the DOM In the t...
    szhielelp阅读 5,355评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,084评论 19 139
  • React 是一个用于构建用户界面的 JAVASCRIPT 库。React主要用于构建UI,很人多认为 React...
    阳明先生1208阅读 5,616评论 0 4
  • 小沙毕业了。 高考发挥得不好不坏,考上了二本。专业选得符合时代潮流,电子商务。四年书读下来,成绩中等得不能再中等,...
    四月芳菲阅读 1,715评论 0 0

友情链接更多精彩内容