网上关于react setState的结论很多,好比:react
但你是否真的了解setState背后的机制?真的是setState触发的刷新吗?
废话不说,先上图app
组件挂载后,setState通常是经过DOM交互事件触发。这里以click
为例,其余也同样。函数
代码很简单this
import React, {Component} from 'react'; class MyInfo extends Component{ constructor(props,context){ super(props,context); this.state = { age:1 } } _grow(age){ age++ this.setState({ age:age }) } render(){ const {age} = this.state return ( <div> 个人年龄是{age} <button onClick={this._grow.bind(this,age)}>点击涨一岁</button> </div> ) } } export default MyInfo;
咱们点击button按钮时,到底发生了什么?ReactEventListener会触发dispatchEvent方法。(具体怎么触发是事件机制的事,这里不深究)spa
dispatchEvent: function (topLevelType, nativeEvent) { if (!ReactEventListener._enabled) { return; } var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType, nativeEvent); try { // Event queue being processed in the same cycle allows // `preventDefault`. ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); } finally { TopLevelCallbackBookKeeping.release(bookKeeping); } }
能够看到这里有个ReactUpdates.batchedUpdates
方法。咱们跟进去看看prototype
function batchedUpdates(callback, a, b, c, d, e) { ensureInjected(); return batchingStrategy.batchedUpdates(callback, a, b, c, d, e); }
能够发现这里调用了batchingStrategy
的方法。这又是什么鬼,其实这是注入进来的ReactDefaultBatchingStragy
(这里插一句,React大量运用了注入机制,这样每次注入的都是同一个实例化对象,防止屡次实例化。)
到这边就已经开启了批量更新模式
继续看,code
batchedUpdates: function(callback, a, b, c, d, e) { var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; ReactDefaultBatchingStrategy.isBatchingUpdates = true; // The code is written this way to avoid extra allocations if (alreadyBatchingUpdates) { return callback(a, b, c, d, e); } else { return transaction.perform(callback, null, a, b, c, d, e); } },
transaction.perform
执行了一个事务。事务其余文章说的不少我就不详细解释了。大概就是,transaction在执行perform以前会执行特性的initialize方法,而后执行传进去的callback,以后会执行close方法,是否是似曾相识?没错,高阶函数或者高阶组件都是这路数。
在ReactDefaultBatchingStragy
里能够发现orm
var transaction = new ReactDefaultBatchingStrategyTransaction(); //在事务结束时清理一下标识 var RESET_BATCHED_UPDATES = { initialize: emptyFunction, close: function() { ReactDefaultBatchingStrategy.isBatchingUpdates = false; }, }; // 在事务结束时执行flushBatchedUpdates方法,这个方法就是 state 更新的核心代码了。 var FLUSH_BATCHED_UPDATES = { initialize: emptyFunction, close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates), }; var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES]; function ReactDefaultBatchingStrategyTransaction() { this.reinitializeTransaction(); } Object.assign(ReactDefaultBatchingStrategyTransaction.prototype, Transaction, { getTransactionWrappers: function() { return TRANSACTION_WRAPPERS; }, });
能够发现这个transition有两个wrapper,主要看FLUSH_BATCHED_UPDATES
对象
目前走完了图中的第一行,有点晕的能够对着图回顾一下。blog
-
是否是发现哪里不对?是的,到如今咱们的setState还没执行呢!
接着上文,咱们首先看看transition的两个initailize方法,发现时两个空函数。跳过
接着就是perform须要执行的逻辑了。再次放出代码
ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
也就是执行这边的handleTopLevelImpl
。正是在这边调用DOM事件对应的回调方法。也就是例子中_grow
在这时候调用。
而后是setState方法。这里和大部分书和文章说的差很少。抛开细节就是将state的变化和对应的回调函数放置到_pendingStateQueue
,和_pendingCallback
中。
而后把须要更新的组件放到dirtyComponents序列中。
重点来了:
注意注意!!!!
setState历来不负责更新操做。它的工做只是把state,和callback放进序列,而且把要更新的组件放到dirtyComponents序列
还记得吗?咱们还在ReactDefalutBatchingStragy
的事务中,perform执行完了,还要执行close。
真正执行更新方法的是close里面的flushBatchedUpdates
。鉴于文章长度,其余的能够看图理解