在代码中调用setState函数之后,React 会将传入的参数对象与组件当前的状态合并,然后触发所谓的调和过程(Reconciliation)。经过调和过程,React 会以相对高效的方式根据新的状态构建 React 元素树并且着手重新渲染整个UI界面。在 React 得到元素树之后,React 会自动计算出新的树与老树的节点差异,然后根据差异对界面进行最小化重渲染。在差异计算算法中,React 能够相对精确地知道哪些位置发生了改变以及应该如何改变,这就保证了按需更新,而不是全部重新渲染。
...
constructor(props) {
super(props)
this.state = {
val: 0
}
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log("componentDidMount:"+this.state.val);
this.setState({val: this.state.val + 1});
console.log("componentDidMount:"+this.state.val);
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log("componentDidMount setTimeout:"+this.state.val);
this.setState({val: this.state.val + 1});
console.log("componentDidMount setTimeout:"+this.state.val);
})
}
componentDidUpdate() {
console.log("componentDidUpdate:"+this.state.val);
}
...
// 打印结果
componentDidMount:0
componentDidMount:0
componentDidUpdate:1
componentDidUpdate:2
componentDidMount setTimeout:2
componentDidUpdate:3
componentDidMount setTimeout:3
ReactComponent.prototype.setState = function(partialState, callback) {
!(
typeof partialState === "object" ||
typeof partialState === "function" ||
partialState == null
)
? "development" !== "production"
? invariant(
false,
"setState(...): takes an object of state variables to update or a function which returns an object of state variables."
)
: _prodInvariant("85")
: void 0;
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, "setState");
}
};
function enqueueUpdate(component) {
// ...
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
dirtyComponents.push(component);
}
// 若 isBatchingUpdates 为 true,则把当前组件(即调用了 setState 的组件)放入 dirtyComponents 数组中;
// 否则 batchUpdate 所有队列中的更新。
var batchingStrategy = {
isBatchingUpdates: false,
batchedUpdates: function(callback, a, b, c, d, e) {
// ...
batchingStrategy.isBatchingUpdates = true;
// Transaction(事务)
// 一个所谓的 Transaction 就是将需要执行的 method 使用 wrapper 封装起来,
// 再通过 Transaction 提供的 perform 方法执行。而在 perform 之前,
// 先执行所有 wrapper 中的 initialize 方法;
// perform 完成之后(即 method 执行后)再执行所有的 close 方法。
// 一组 initialize 及 close 方法称为一个 wrapper
transaction.perform(callback, null, a, b, c, d, e);
}
};
由以上代码可知调用setState 将我们传入的partialState添加到一个叫做_pendingStateQueue的队列中去存起来,然后执行一个enqueueUpdate方法, 并把当前组件(即调用了 setState 的组件)放入 dirtyComponents 数组中在某一时刻做批量更新,所以console.log("componentDidMount:"+this.state.val)
的结果为0。
this.setState(partialState1);
this.setState(partialState2);
// 连续调用
// 事务中会将partialState1和partialState2合并处理
类似于 Object.assign({}, partialState1, partialState2)
constructor(props) {
super(props);
this.state = {
val5: 0
};
}
componentDidMount() {
this.setState((state, props) => {
return {
val5: state.val5 + 1
};
});
this.setState((state, props) => {
return {
val5: state.val5 + 1
};
});
console.log("componentDidMount val5:" + this.state.val5);
}
componentDidUpdate() {
console.log("componentDidUpdate val5:" + this.state.val5);
}
// 结果
componentDidMount val5:0
componentDidUpdate val5:2
_processPendingState: function (props, context) {
var inst = this._instance; // _instance保存了Constructor的实例,即通过ReactClass创建的组件的实例
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;
}
for循环遍历了_pendingStateQueue中所有保存的状态,对于每一个状态进行处理,处理时首先判断保存的是function还是object。若是function,就在inst的上下文中执行该匿名函数,该函数返回一个代表新state的object,然后执行assign将其与原有的state合并;若是object,则直接与state合并。所以生成componentDidUpdate val5:2
的结果