setState源码分析
简述setState执行过程,具体源码解析可以根据此文章看《深入React技术栈》 本文不适用于js初学者 起码需要知道 闭包 立即执行函数 js事件队列等概念再进行阅读
原理简述
首先react有事务得概念, 类似于生命周期。将自定义函数 传入事务封装函数中 执行被封装得自定义函数时回先执行 封装事务时定义得init方法 然后在执行自定义函数 再执行close方法(开始 执行 结束)
当我们调用setState过程中,我们往往是在生命周期函数或者是react封装得事件当中,当react组件处于此状态当中时,组件在虚拟dom中具有属性是否正在批量更新
isBatchingUpdates
设置为true接着我们去执行setState setState也时被事务封装过得 他会先判断组件是否在处于正在更新 如果是true就该state放到批量更新队列当中。同时将回调放入回调队列.如果不是正在更新中 就直接执行
接着执行上一个事务得close 将正在批量更新属性设置为false
执行setState 事务中得close 既队列执行批量更新队列
这也就解释了为什么异步函数中的setState会变直接执行。因为此时上一个事务已经执行结束,所以此时isBatchingUpdates已经是false 所以就会直接执行
样例代码分析
import React from 'react';
class ClassComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 1
}
}
componentDidMount() {
console.log('类组件state初始值', this.state.count)
this.setState({count: 2})
console.log('类组件第一个state', this.state.count)
setTimeout(() => {
console.log('类组件第二个state',this.state.count);
this.setState({count: 3})
console.log('类组件第三个state',this.state.count);
this.setState({count: 4})
console.log('类组件第四个state',this.state.count);
}, 0)
setTimeout(() => {
console.log('类组件第五个state',this.state.count);
this.setState({count: 5})
console.log('类组件第六个state',this.state.count);
this.setState({count: 6})
console.log('类组件第七个state',this.state.count);
}, 0)
this.setState({count: 7})
console.log('类组件第八个setState', this.state.count)
}
render() {
console.log('类组件渲染')
return (
<div>
<div>
类组件最终显示结果 {
this.state.count
} </div>
</div>
);
}
}
export default ClassComponent;
控制台打印结果
类组件渲染
类组件state初始值 1
类组件第一个state 1
类组件第八个setState 1
类组件渲染
类组件第二个state 7
类组件渲染
类组件第三个state 3
类组件渲染
类组件第四个state 4
类组件第五个state 4
类组件渲染
类组件第六个state 5
类组件渲染
类组件第七个state 6
至于为什么显示这些,自行套入原理中所述过程
React hocks 中不一样的表现
但是类似代码用react hocksapi重新实现一遍 会有不一样表现
import React ,{useState,useEffect} from 'react';
export default function TestReactHock(){
const [count,setCount] = useState('1');
useEffect(()=>{
console.log('纯组件state初始值', count)
setCount(2)
console.log('纯组件第一个state', count)
setTimeout(() => {
console.log('纯组件第二个state', count);
setCount(3)
console.log('纯组件第三个state', count);
setCount(4)
console.log('纯组件第四个state', count);
}, 0)
setTimeout(() => {
console.log('纯组件第五个state', count);
setCount(5)
console.log('纯组件第六个state', count);
setCount(6)
console.log('纯组件第七个state', count);
}, 0)
setCount( 7)
console.log('纯组件第八个setState', count);
},[])
console.log('纯组件渲染',count);
return (
<div>
最终的结果是{count}
</div>
)
}
最终结果为
纯组件渲染<br/>
纯组件state初始值 1<br/>
纯组件第一个state 1<br/>
纯组件第八个setState 1<br/>
纯组件渲染<br/>
纯组件第二个state 1<br/>
纯组件渲染<br/>
纯组件第三个state 1<br/>
纯组件渲染<br/>
纯组件第四个state 1<br/>
纯组件第五个state 1<br/>
纯组件渲染<br/>
纯组件第六个state 1<br/>
纯组件渲染<br/>
纯组件第七个state 1<br/>
** 可以看到 再react hocks 中的useEffect中 无论是同步还是异步函数中 获取到的state或者props值都是最开始传入的state 或者props** ** 除此之外 再更新逻辑上和类组件并无异同**
问题原因
实际上useEffect传入的函数 中的state值是通过闭包的形式传入的,我这里简单实现了一个版本
var c = 0; // 理解为state
function setC(newC){
c = newC
} // 理解为setState
function b(){ // b是useEffect
(function dd(i) { // dd(i) 就是给effect传入的函数
setTimeout(function() {
setC(2)
console.log(i)
}, 0);
setTimeout(function() {
setC(3)
console.log(i)
}, 0);
})(c); // 用闭包的形式传入i
}
b()
console.log(c)
结果就是 00 3
Last updated
Was this helpful?