转自IMWeb社区,做者:黄qiong, 原文连接
学过react的人都知道,setState在react里是一个很重要的方法,使用它能够更新咱们数据的状态,本篇文章从简单使用到深刻到setState的内部,全方位为你揭开setState的神秘面纱~react
setState(updater, callback)
这个方法是用来告诉react组件数据有更新,有可能须要从新渲染。它是异步的,react一般会集齐一批须要更新的组件,而后一次性更新来保证渲染的性能,因此这就给咱们埋了一个坑:web
那就是在使用setState
改变状态以后,马上经过this.state
去拿最新的状态每每是拿不到的。数组
因此第一个使用要点就是:若是你须要基于最新的state作业务的话,能够在componentDidUpdate
或者setState
的回调函数里获取。(注:官方推荐第一种作法)bash
// setState回调函数
changeTitle: function (event) {
this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
// Call API with the updated value
}
复制代码
设想有一个需求,须要在在onClick里累加两次,以下app
onClick = () => {
this.setState({ index: this.state.index + 1 });
this.setState({ index: this.state.index + 1 });
}
复制代码
在react眼中,这个方法最终会变成异步
Object.assign(
previousState,
{index: state.index+ 1},
{index: state.index+ 1},
...
)
复制代码
因为后面的数据会覆盖前面的更改,因此最终只加了一次.因此若是是下一个state依赖前一个state的话,推荐给setState传function函数
onClick = () => {
this.setState((prevState, props) => {
return {quantity: prevState.quantity + 1};
});
this.setState((prevState, props) => {
return {quantity: prevState.quantity + 1};
});
}
复制代码
以上是使用setState的两个注意事项,接下来咱们来看看setState被调用以后,更新组件的过程,下面是一个简单的流程图。性能
下面来逐步的解析图里的流程。ui
ReactComponent.prototype.setState = function (partialState, callback) {
// 将setState事务放进队列中
this.updater.enqueueSetState(this, partialState);
if (callback) {
this.updater.enqueueCallback(this, callback, 'setState');
}
};
复制代码
这里的partialState能够传object,也能够传function,它会产生新的state以一种Object.assgine()
的方式跟旧的state进行合并。this
enqueueSetState: function (publicInstance, partialState) {
// 获取当前组件的instance
var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');
// 将要更新的state放入一个数组里
var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
queue.push(partialState);
// 将要更新的component instance也放在一个队列里
enqueueUpdate(internalInstance);
}
复制代码
这段代码能够得知,enqueueSetState 作了两件事: 一、将新的state放进数组里 二、用enqueueUpdate来处理将要更新的实例对象
function enqueueUpdate(component) {
// 若是没有处于批量建立/更新组件的阶段,则处理update state事务
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate, component);
return;
}
// 若是正处于批量建立/更新组件的过程,将当前的组件放在dirtyComponents数组中
dirtyComponents.push(component);
}
复制代码
由这段代码能够看到,当前若是正处于建立/更新组件的过程,就不会马上去更新组件,而是先把当前的组件放在dirtyComponent里,因此不是每一次的setState都会更新组件~。
这段代码就解释了咱们经常据说的:setState是一个异步的过程,它会集齐一批须要更新的组件而后一块儿更新。
而batchingStrategy 又是个什么东西呢?
var ReactDefaultBatchingStrategy = {
// 用于标记当前是否出于批量更新
isBatchingUpdates: false,
// 当调用这个方法时,正式开始批量更新
batchedUpdates: function (callback, a, b, c, d, e) {
var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
ReactDefaultBatchingStrategy.isBatchingUpdates = true;
// 若是当前事务正在更新过程在中,则调用callback,既enqueueUpdate
if (alreadyBatchingUpdates) {
return callback(a, b, c, d, e);
} else {
// 不然执行更新事务
return transaction.perform(callback, null, a, b, c, d, e);
}
}
};
复制代码
这里注意两点: 一、若是当前事务正在更新过程当中,则使用enqueueUpdate
将当前组件放在dirtyComponent
里。 二、若是当前不在更新过程的话,则执行更新事务。
/**
* <pre>
* wrappers (injected at creation time)
* + +
* | |
* +-----------------|--------|--------------+
* | v | |
* | +---------------+ | |
* | +--| wrapper1 |---|----+ |
* | | +---------------+ v | |
* | | +-------------+ | |
* | | +----| wrapper2 |--------+ |
* | | | +-------------+ | | |
* | | | | | |
* | v v v v | wrapper
* | +---+ +---+ +---------+ +---+ +---+ | invariants
* perform(anyMethod) | | | | | | | | | | | | maintained
* +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | | | | | | | | | | | |
* | +---+ +---+ +---------+ +---+ +---+ |
* | initialize close |
* +-----------------------------------------+
* </pre>
*/
复制代码
简单说明一下transaction对象,它暴露了一个perform的方法,用来执行anyMethod,在anyMethod执行的前,须要先执行全部wrapper的initialize方法,在执行完后,要执行全部wrapper的close方法,就辣么简单。
在ReactDefaultBatchingStrategy.js,tranction 的 wrapper有两个 FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES
var RESET_BATCHED_UPDATES = {
initialize: emptyFunction,
close: function () {
ReactDefaultBatchingStrategy.isBatchingUpdates = false;
}
};
var FLUSH_BATCHED_UPDATES = {
initialize: emptyFunction,
close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];
复制代码
能够看到,这两个wrapper的initialize
都没有作什么事情,可是在callback执行完以后,RESET_BATCHED_UPDATES 的做用是将isBatchingUpdates置为false, FLUSH_BATCHED_UPDATES 的做用是执行flushBatchedUpdates,而后里面会循环全部dirtyComponent,调用updateComponent来执行全部的生命周期方法,componentWillReceiveProps, shouldComponentUpdate, componentWillUpdate, render, componentDidUpdate 最后实现组件的更新。
以上即为setState的实现过程,最后仍是用一个流程图在作一个总结吧~
参考文档: