react 常见setState的原理解析
程序员文章站
2024-03-02 16:04:28
...
首先引入一个栗子
class Example extends React.Component {
constructor() {
super();
this.state = {
val: 0
};
}
componentDidMount() {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 1 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 2 次 log
setTimeout(() => {
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 3 次 log
this.setState({val: this.state.val + 1});
console.log(this.state.val); // 第 4 次 log
}, 0);
}
render() {
return null;
}
};
4次log的值 分别为 0 0 2 3
setState 干了什么
说一下批量更新
解读为什么直接修改this.state无效
要知道setState本质是通过一个队列机制实现state更新的。执行setState时,会将需要更新的state合并后放入状态队列,而不会立刻更新state,
队列机制可以批量更新state。如果不通过setState而直接修改this.state,那么这个state不会放入状态队列中
,下次调用setState时对状态队列进行合并时,会忽略之前直接被修改的state,这样我们就无法合并了,而且实际也没有把你想要的state更新上去。
什么是批量更新 Batch Update
在一些mv*框架中,,就是将一段时间内对model的修改批量更新到view的机制。比如那前端比较火的React、vue(nextTick机制,视图的更新以及实现)为例。
vue的nextTick机制 https://www.cnblogs.com/hity-tt/p/6729118.html
html5新特性变动观察器 http://www.cnblogs.com/jscode/p/3600060.html
消息进程 http://www.ruanyifeng.com/blog/2013/10/event_loop.html
vue的批量更新体现
- Mutation Observer(变动观察器)是监视DOM变动的接口。当DOM对象树发生任何变动时,Mutation Observer会得到通知。
- 概念上,它很接近事件。可以理解为,当DOM发生变动会触发Mutation Observer事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说DOM发生变动立刻会触发相应的事件;
-
Mutation Observer则是异步触发,DOM发生变动以后,并不会马上触发,而是要等到当前所有DOM操作都结束后才触发
。 - 这样设计是为了应付DOM变动频繁的情况。举例来说,如果在文档中连续插入1000个段落(p元素),会连续触发1000个插入事件,执行每个事件的回调函数,这很可能造成浏览器的卡顿;
- 而Mutation Observer完全不同,只在1000个段落都插入结束后才会触发,而且只触发一次。
setState之后发生的事情
- 在官方的描述中,setState操作并不保证是同步的,也可以认为是异步的。
- React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。如果这一系列过程立刻发生在每一个setState之后,就可能会有性能问题。
- 在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果。
总结
- 通过setState去更新this.state,不要直接操作this.state,请把它当成不可变的。
-
调用setState更新this.state不是马上生效的,它是异步滴
,所以不要天真以为执行完setState后this.state就是最新的值了。 多个顺序执行的setState不是同步地一个一个执行滴,会一个一个加入队列,然后最后一起执行,即批处理
如何知道state已经被更新
传入回调函数
setState({
index: 1
}}, function(){
console.log(this.state.index);
})
// 在钩子函数中体现
componentDidUpdate(){
console.log(this.state.index);
}
setState的另外一种方式 (需要使用上一次的state的值)
在setState的第一个参数中传入function,该function会被压入调用栈中,在state真正改变后,按顺序回调栈里面的function。
该function的第一个参数为上一次更新后的state。这样就能确保你下一次的操作拿到的是你预期的值
lass Com extends React.Component{
constructor(props){
super(props);
this.state = {
index: 0
}
this.add = this.add.bind(this);
}
add(){
this.setState(prevState => {
return {index: prevState.index + 1};
});
this.setState(prevState => {
return {index: prevState.index + 1};
});
}
}
注意点
- setState可能会引发不必要的渲染(renders)
- setState无法完全掌控应用中所有组件的状态