前言
在深究 React 的 setState 原理的时候,我们先要考虑一个问题:setState 是异步的吗?
首先以 class component 为例,请看下述代码(demo-0)
class App extends React.Component {
state = {
count: 0
}
handleCountClick = () => {
this.setState({
count: this.state.count + 1
});
console.log(this.state.count);
}
render() {
return (
<div className='app-box'>
<div onClick={
this.handleCountClick}>the count is {
this.state.count}</div>
</div>
)
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
count
初始值为 0,当我们触发handleCountClick
事件的时候,执行了count + 1
操作,并打印了count
,此时打印出的count
是多少呢?答案不是 1 而是 0
类似的 function component 与 class component 原理一致。现在我们以 function component 为例,请看下述代码 (demo-1)
const App = function () {
const [count, setCount] = React.useState(0);
const handleCountClick = () => {
setCount((count) => {
return count + 1;
});
console.log(count);
}
return <div className='app-box'>
<div onClick={
handleCountClick}>the count is {
count}</div>
</div>
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
同样的,这里打印出的 count
也为 0
相信大家都知道这个看起来是异步的现象,但他真的是异步的吗?
为什么 setState
看起来是『异步』的?
首先得思考一个问题:如何判断这个函数是否为异步?
最直接的,我们写一个 setTimeout
,打个 debugger 试试看
我们都知道 setTimeout
里的回调函数是异步的,也正如上图所示,chrome 会给 setTimeout
打上一个 async
的标签。
接下来我们 debugger setState
看看
React.useState
返回的第二个参数实际就是这个 dispatchSetState
函数(下文细说)。但正如上图所示,这个函数并没有 async
标签,所以 setState
并不是异步的。
那么抛开这些概念来看,上文中 demo-1 的类似异步的现象是怎么发生的呢?
简单的来说,其步骤如下所示。基于此,我们接下来更深入的看看 React 在这个过程中做了什么
从 first paint 开始
first paint 就是『首次渲染』,为突出显示,就用英文代替。
- 这里先简单看一下
App
往下的 fiber tree 结构。每个 fiber node 还有一个return
指向其 parent fiber node,这里就不细说了
我们都知道 React 渲染的时候,得遍历一遍 fiber tree,当走到 App 这个 fiber node 的时候发生了什么呢?
接下来我们看看详细的代码(这里的 workInProgres