React中setState真的是异步的吗
|
在学习react的过程中几乎所有学习材料都会反复强调一点setState是异步的,来看一下react官网对于setState的说明。
将
一个很经典的例子如下 // state.count 当前为 0
componentDidMount(){
this.setState({count: state.count + 1});
console.log(this.state.count)
}
如果你熟悉react,你一定知道最后的输出结果是0,而不是1。 然而事实真的是这样吗? 我们再来看一个例子 class Hello extends Component {
constructor(props) {
super(props);
this.state = { counter: 0 };
}
render() {
return <div onClick={this.onClick.bind(this)}>点我</div>;
}
componentDidMount() {
//手动绑定mousedown事件
ReactDom.findDOMNode(this).addEventListener(
"mousedown",this.onClick.bind(this)
);
//延时调用onclick事件
setTimeout(this.onClick.bind(this),1000);
}
onClick(event) {
if (event) {
console.log(event.type);
} else {
console.log("timeout");
}
console.log("prev state:",this.state.counter);
this.setState({
counter: this.state.counter + 1
});
console.log("next state:",this.state.counter);
}
}
export default Hello;
在这个组件中采用3中方法更新state
你可以猜到结果吗?输出结果是: timeout "prev state:" 0 "next state:" 1 mousedown "prev state:" 1 "next state:" 2 click "prev state:" 2 "next state:" 2 结果似乎有点出人意料,三种方式只有在div上绑定的onClick事件输出了可以证明setState是异步的结果,另外两种方式显示setState似乎是同步的。 React的核心成员Dan Abramov也在一次回复中提到 <font color='red'>这到底是这么回事?</font> 话不多说,直接上源码,如果你对react源码有一定了解可以接着往下看,如果没有,可以直接跳到结论(以下分析基于react15,16版本可能有出入)。 setState异步的实现在componentWillMount中调用setState//代码位于ReactBaseClasses
* @param {partialState} 设置的state参数
* @param {callback} 设置state后的回调
ReactComponent.prototype.setState = function(partialState,callback) {
invariant(
typeof partialState === 'object' ||
typeof partialState === 'function' ||
partialState == null,'setState(...): takes an object of state variables to update or a ' +
'function which returns an object of state variables.',);
this.updater.enqueueSetState(this,partialState);
if (callback) {
this.updater.enqueueCallback(this,callback,'setState');
}
};
在 //代码位于ReactUpdateQueue.js
* @param {publicInstance} 需要重新渲染的组件实例
* @param {partialState} 设置的state
* @internal
enqueueSetState: function(publicInstance,partialState) {
//省略部分代码
//从组件列表中找到并返回需渲染的组件
var internalInstance = getInternalInstanceReadyForUpdate(
publicInstance,'setState',);
if (!internalInstance) {
return;
}
//state队列
var queue =
internalInstance._pendingStateQueue ||
(internalInstance._pendingStateQueue = []);
//将新的state放入队列
queue.push(partialState);
enqueueUpdate(internalInstance);
},
在 //代码位于ReactUpdateQueue.js
function enqueueUpdate(internalInstance) {
ReactUpdates.enqueueUpdate(internalInstance);
}
//代码位于ReactUpdates.js
function enqueueUpdate(component) {
ensureInjected();
// Various parts of our code (such as ReactCompositeComponent's
// _renderValidatedComponent) assume that calls to render aren't nested;
// verify that that's the case. (This is called by each top-level update
// function,like setState,forceUpdate,etc.; creation and
// destruction of top-level components is guarded in ReactMount.)
if (!batchingStrategy.isBatchingUpdates) {
batchingStrategy.batchedUpdates(enqueueUpdate,component);
return;
}
dirtyComponents.push(component);
if (component._updateBatchNumber == null) {
component._updateBatchNumber = updateBatchNumber + 1;
}
}
这段代码就是实现
这就解释了在componentDidMount中调用setState并不会立即更新state,因为正处于一个更新流程中,isBatchingUpdates为true,所以只会放入dirtyComponents中等待稍后更新。 事件中的调用setState那么在事件中调用 dispatchEvent: function (topLevelType,nativeEvent) {
// disable了则直接不回调相关方法
if (!ReactEventListener._enabled) {
return;
}
var bookKeeping = TopLevelCallbackBookKeeping.getPooled(topLevelType,nativeEvent);
try {
// 放入
ReactUpdates.batchedUpdates(handleTopLevelImpl,bookKeeping);
} finally {
TopLevelCallbackBookKeeping.release(bookKeeping);
}
}
看到了熟悉的batchedUpdates方法,只是调用方换成了ReactUpdates,再进入ReactUpdates.batchedUpdates。 function batchedUpdates(callback,a,b,c,d,e) {
ensureInjected();
return batchingStrategy.batchedUpdates(callback,e);
}
豁然开朗,原来在事件的处理中也是通过同样的事务完成的,当进入事件处理流程后,该事务的isBatchingUpdates为true,如果在事件中调用setState方法,也会进入dirtyComponent流程。 原生事件绑定和setTimeout中setState在回过头来看同步的情况,原生事件绑定不会通过合成事件的方式处理,自然也不会进入更新事务的处理流程。setTimeout也一样,在setTimeout回调执行时已经完成了原更新组件流程,不会放入dirtyComponent进行异步更新,其结果自然是同步的。 顺便提一下,在更新组建时,将更新的state合并到原state是在componentWillUpdate之后,render之前,所以在componentWillUpdate之前设置的 总结1.在组件生命周期中或者react事件绑定中,setState是通过异步更新的。 这个结果并不说明setState异步执行的说法是错误的,更加准确的说法应该是setState不能保证同步执行。 Dan Abramov也多次提到今后会将setState改造为异步的,从js conf中提到的suspend新特新也印证了这一点。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
