React setState源码阅读

论坛 期权论坛     
选择匿名的用户   2021-5-23 16:54   223   0
<div class="article-content">
<p>本文基于React15总结,最新React16可能有一些出入,望周知!!!</p>
<h3 class="heading">setState真的是异步的吗 ?</h3>
<p><strong>举个?</strong></p>
<pre class="blockcode"><code class="hljs bash copyable">class Example extends React.Component {
  <span class="hljs-function"><span class="hljs-title">constructor</span></span>() {
    super();
    this.state &#61; {
      val: 0
    };
  }
  
  <span class="hljs-function"><span class="hljs-title">componentDidMount</span></span>() {
    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);    // 第 1 次 <span class="hljs-built_in">log</span>

    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);    // 第 2 次 <span class="hljs-built_in">log</span>

    <span class="hljs-built_in">set</span>Timeout(() &#61;&gt; {
      this.setState({val: this.state.val &#43; 1});
      console.log(this.state.val);  // 第 3 次 <span class="hljs-built_in">log</span>

      this.setState({val: this.state.val &#43; 1});
      console.log(this.state.val);  // 第 4 次 <span class="hljs-built_in">log</span>
    }, 0);
  }

  <span class="hljs-function"><span class="hljs-title">render</span></span>() {
    <span class="hljs-built_in">return</span> null;
  }
};
<span class="copy-code-btn">复制代码</span></code></pre>
<p>问上述代码中 4 次 console.log 打印出来的 val 分别是多少? 不卖关子,先揭晓答案,4 次 log 的值分别是:0、0、2、3。 若结果和你心中的答案不完全相同,那下面的内容你可能会感兴趣。 同样的 setState 调用,为何表现和结果却大相径庭呢?让我们先看看 setState 到底干了什么。</p>
<h3 class="heading">先放结论:</h3>
<ul><li>setState 只在<strong>合成事件和钩子函数中是“异步”的</strong>,在<strong>原生事件和setTimeout 中都是同步</strong>的。</li><li>setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。</li><li>setState的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,<strong>在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。</strong></li></ul>
<p>所以基于上述结论,如果想要实现上述代码中 4 次 console.log 打印出来的 val 分别是1、2、3、4。可以实现如下:</p>
<pre class="blockcode"><code class="hljs bash copyable"><span class="hljs-built_in">set</span>Timeout(() &#61;&gt; {
    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);  // 1

    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);  // 2

    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);  // 3

    this.setState({val: this.state.val &#43; 1});
    console.log(this.state.val);  // 4
}, 0);
<span class="copy-code-btn">复制代码</span></code></pre>
<p>或者</p>
<pre class="blockcode"><code class="hljs bash copyable">this.setState((prevState) &#61;&gt; {
    <span class="hljs-built_in">return</span> { count: prevState.val &#43; 1 }
})
console.log(this.state.val);  // 1

this.setState((prevState) &#61;&gt; {
    <span class="hljs-built_in">return</span> { count: prevState.val &#43; 1 }
})
console.log(this.state.val);  // 2

this.setState((prevState) &#61;&gt; {
    <span class="hljs-built_in">return</span> { count: prevState.val &#43; 1 }
})
console.log(this.state.val);  // 3

this.setState((prevState) &#61;&gt; {
    <span class="hljs-built_in">return</span> { count: prevState.val &#43; 1 }
})
console.log(this.state.val);  // 4
<span class="copy-code-btn">复制代码</span></code></pre>
<h3 class="heading">setState 干了什么</h3>
<p></p>
<figure>
  <figcaption></figcaption>
</figure>
<p></p>
<p>上面这个流程图是一个简化的 setState 调用栈,setState 方法由父类 Component 提供(因为组件本身继承自React.Component),是 React 组件修改局部状态的方法。</p>
<pre class="blockcode"><code class="hljs bash copyable">// src/isomorphic/modern/class/ReactComponent.js
ReactComponent.prototype.setState &#61; <span class="hljs-keyword">function</span>(partialState, callback) {
  // ...
  this.updater.enqueueSetState(this, partialState);
  <span class="hljs-keyword">if</span> (callback) {
    this.updater.enqueueCallback(this, callback);
  }
};
<span class="copy-code-btn">复制代码</span></code></pre>
<pre class="blockcode"><code class="hljs bash copyable">// src/renderers/shared/reconciler/ReactUpdateQueue.js
enqueueSetState: <span class="hljs-keyword">function</span>(publicInstance, partialState) {
  // 获取 ReactComponent 组件对象(这里的组件对象指的是调用了this.setState的组件)
  va
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP