阅读源码已经有一段时间了,基本都是利用工作之余看的,有什么收获呢?除了更加了解框架的底层实现,更重要的大概就是自信了!技术自信对于每个研发来说是非常重要的,这会影响你在工作过程中的表现和状态。
转回话题,setState可以说在React中是比较灵魂的角色,无论平时开发还是面试中,都会高频问题。初学者在使用setState的时候会踩一些坑,本文针对一些setState内部原理进行源码范围的探索吧~!
基本使用
setState的写法可以分为两类:
setState(updater[, callback]):第一个参数是一个updater函数;第二个参数是个回调函数(可选)
setState(stateChange[, callback]):第一个参数是一个对象;第二个参数同上(可选)
第二个参数都一样,是一个可选的回调函数,其将会在setState执行完成同时组件被重渲之后再执行。通常,对于这类逻辑,我们推荐使用componentDidUpdate来处理。
当state更新值依赖state和props时,使用第一种形式
this.setState({count=> this.state.count+1})
this.setState((prevState, props) => {
return {count: prevState.count + props.step};
});
setState之后,调用的生命周期链路:setState=>shouldComponentUpdate=>componentWillUpdate=>re-render=>componentDidUpdate
如果我们想阻止re-render,可以在shouldComponentUpdate中返回false
React官方建议把State当作是不可变对象,也就是说,当你有修改this.state的值的冲动的时候,你应该做的是:重新创建一个新值来赋给this.state。
原因
- 不可变对象方便管理和调试,了解更多可参考这里
- shouldComponentUpdate PureComponent 都是通过浅比较(对象引用) 避免频繁re-render
尤其是复杂数据类型,常见会改变原数组的方法:push pop shift unshift splice等
demo
import * as React from "react";
class SetStateDemo extends React.Component {
constructor(props) {
super(props);
}
state = {
count: 0,
};
componentDidMount() {
console.log("setState=====>componentDidMount");
this.setState({ count: this.state.count + 1 });
console.log('setState=====>1',this.state.count); // 0
this.setState({ count: this.state.count + 1 });
console.log('setState=====>2',this.state.count); // 0
setTimeout(_ => {
this.setState({ count: this.state.count + 1 });
console.log('setState=====>3',this.state.count); // 2
this.setState({ count: this.state.count + 1 });
console.log('setState=====>4',this.state.count); // 3
}, 0);
}
increment = () => {
console.log("increment");
this.setState({ count: this.state.count + 1 });
console.log('setState=====>4',this.state.count); // 3
this.setState({ count: this.state.count + 1 });
console.log(this.state.count); // 3
};
render() {
console.log("render");
return <button onClick={this.increment}>{this.state.count}</button>;
}
}
export default SetStateDemo;
打印可以看到:
- setState更新之后,不会立即拿到更新后的值,说明setState是异步的(这里指合成事件和生命周期中)
- 多次更新同一个状态,会合并成一次更新
- setTimeout或者原生事件中,setState之后可以立即拿到更新后的值
关于setState的疑惑
- state对象保存在哪儿?
- 为什么有时连续多次setState只有一次生效?
- state更新到底在哪一阶段实现?
- 执行完setState获取state的值能获取到吗?
- setState是同步的还是异步的?
- setState的callback执行时机
源码解读
点击Performance 查看面板
很清晰,可以看到setState的调用栈情况。
我们大概搂一眼,可以看到setState => enqueueState => scheduleUpdateOnFiber 之后一系列的动作就是 render和commit工作,render和commit阶段讲解 移步XXXX
我们的源码探索之路沿着这条调用栈看看setState到底做了什么?
来吧!
一旦用户的交互产生了更新,那么就会产生一个update对象去承载新的状态。多个update会连接成一个环装链表:updateQueue,挂载fiber上, 然后在该fiber的beginWork阶段会循环该updateQueue,依次处理其中的update,这是处理更新的大致过程