# classState
# class组件setState函数是同步还是异步,可不可能是同步
我们默认react内部代码执行到
performWork、performWorkOnRoot、performSyncWork、performAsyncWork这四个方法的时候,就是react去update更新并且作用到UI上。
# 一、合成事件中的setState
什么是合成事件,
react为了解决跨平台,兼容性问题,自己封装了一套事件机制,代理了原生的事件,像在jsx中常见的onClick、onChange这些都是合成事件。
function requestWork(root, expirationTime) {
addRootToSchedule(root, expirationTime);
if (isRendering) {
// Prevent reentrancy. Remaining work will be scheduled at the end of
// the currently rendering batch.
return;
}
if (isBatchingUpdates) {
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, false);
}
return;
}
// TODO: Get rid of Sync and use current time?
if (expirationTime === Sync) {
performSyncWork();
} else {
scheduleCallbackWithExpiration(expirationTime);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
从 setState 到 requestWork 是调用 this.setState 的逻辑
所以当你在 increment 中调用 setState 之后去console.log的时候,是属于 try 代码块中的执行,但是由于是合成事件,try代码块执行完state并没有更新,所以你输入的结果是更新前的 state 值,这就导致了所谓的"异步",但是当你的try代码块执行完的时候(也就是你的increment合成事件),这个时候会去执行 finally 里的代码,在 finally 中执行了 performSyncWork 方法,这个时候才会去更新你的 state 并且渲染到UI上。
其实还是和合成事件一样,当 componentDidmount 执行的时候,react内部并没有更新,执行完componentDidmount 后才去 commitUpdateQueue 更新。这就导致你在 componentDidmount 中 setState 完去console.log拿的结果还是更新前的值。
原生事件是指非react合成事件,
原生自带的事件监听 addEventListener,或者也可以用原生js、jq直接 document.querySelector().onclick 这种绑定事件的形式都属于原生事件。所以当你在原生事件中setState后,能同步拿到更新后的state值。
# 四、setTimeout中的setState
比如之前的合成事件,由于你是
setTimeout(_ => { this.setState()}, 0)是在 try 代码块中,当你 try 代码块执行到 setTimeout 的时候,把它丢到列队里,并没有去执行,而是先执行的 finally 代码块,等 finally 执行完了, isBatchingUpdates 又变为了 false ,导致最后去执行队列里的 setState 时候, requestWork 走的是和原生事件一样的 expirationTime === Sync if分支,所以表现就会和原生事件一样,可以同步拿到最新的state值。
# 五、setState中的批量更新
- setState 只在
合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout中都是同步的。 - setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然
可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。
# setState各种使用方法
# 使用上一状态的值
this.setState(prev => ({
visible: true,
detail: { companyId: prev.searchParams.companyId },
title: formatMessage({
id: 'common.add',
}),
}));
this.setState((prevState, props) => {
return {counter: prevState.counter + props.step};
});
2
3
4
5
6
7
8
9
10
11
# 立即使用新的状态值,通过回调实现
this.state = {foo: 1};
this.setState({foo: 123});
console.log(this.state.foo);
// 1
this.state = {foo: 2};
this.setState({foo: 123}, ()=> {
console.log(foo);
// 123
});
2
3
4
5
6
7
8
9
10
11