为何使用setState
-
React中不能直接修改state的值来让界面发生更新,因为React并没有实现类似Vue2中的Object.defineProperty,或是Vue3中的Proxy的方式进行数据劫持,监听数据变化 -
React中必须通过调用setState来告知React数据已经发生了变化 -
setState方法是从Component中继承过来的
setState的两种调用方式
值更新:直接传递一个新的状态对象
this.setState({count:1})
函数更新:传递一个回调函数,接收prevState和props作为参数
this.setState((prevState,props)=>{
return { count:prevState.count + 1 }
})
setState的特性
异步更新
class Example extends React.Component {
state = {
count: 0
};
handleClick = () => {
// 错误示范:直接读取更新后的状态
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 不会立即显示更新后的值
// 正确示范:在回调中读取更新后的状态
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count); // 会显示更新后的值
});
};
}
状态合并
class Counter extends React.Component {
state = {
user:{
name:"test",
age:23
}
};
updateUser = () =>{
// 浅合并,只更新指定的字段
this.setState({
user:{
...this.state.user,// 保留其他字段
age:24
}
})
}
}
批量更新
class Counter extends React.Component {
state = {
count:0
};
handleClick = () =>{
// 这些setState会被批量处理,但是最终count的值只会加1
this.setState({ count:this.state.count + 1})
this.setState({ count:this.state.count + 1})
this.setState({ count:this.state.count + 1})
// 使用函数式更新可以累加,最终count的值为3
this.setState(prevState => ({ count:this.state.count + 1}))
this.setState(prevState => ({ count:this.state.count + 1}))
this.setState(prevState => ({ count:this.state.count + 1}))
}
}
setState源码解析 React V18.0.0
packages/react/src/ReactBaseClasses.js
Component.prototype.setState = function(partialState, callback) {
this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
this.updater
this.updater 是在哪个地方进行赋值的我们暂时不用关心,只需要知道他被赋值为classComponentUpdater
classComponentUpdater
packages/react-reconciler/src/ReactFiberClassComponent.js 我们只需关心生成了update,插入到update队列,enqueueUpdate(fiber, update,lane);然后调用scheduleWork
// 伪代码
const classComponentUpdater = {
...
enqueueSetState(inst, payload, callback) {
const update = createUpdate(expirationTime);
// setState(payload, callback);
update.payload = payload;
update.callback = callback;
// 插入到update队列
enqueueUpdate(fiber, update,lane);
scheduleWork(fiber, expirationTime);
},
...
scheduleWork
packages/react-reconciler/src/ReactFiberScheduler.js
// isWorking、isCommitting是全部变量,在后面我们会具体分析到
if (
!isWorking ||
isCommitting ||
nextRoot !== root
) {
const rootExpirationTime = root.expirationTime;
requestWork(root, rootExpirationTime);
}
requestWork
setState调用原理
React 18之后,默认所有的setState都是异步处理,实现批量更新
实现同步效果,调用react-dom中的flushSync的API,同步效果