欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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 干了什么

react 常见setState的原理解析

说一下批量更新

react 常见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的批量更新体现

  1. Mutation Observer(变动观察器)是监视DOM变动的接口。当DOM对象树发生任何变动时,Mutation Observer会得到通知。
  2. 概念上,它很接近事件。可以理解为,当DOM发生变动会触发Mutation Observer事件。但是,它与事件有一个本质不同:事件是同步触发,也就是说DOM发生变动立刻会触发相应的事件;
  3. Mutation Observer则是异步触发,DOM发生变动以后,并不会马上触发,而是要等到当前所有DOM操作都结束后才触发
  4. 这样设计是为了应付DOM变动频繁的情况。举例来说,如果在文档中连续插入1000个段落(p元素),会连续触发1000个插入事件,执行每个事件的回调函数,这很可能造成浏览器的卡顿;
  5. 而Mutation Observer完全不同,只在1000个段落都插入结束后才会触发,而且只触发一次。

setState之后发生的事情

  1. 在官方的描述中,setState操作并不保证是同步的,也可以认为是异步的。
  2. React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。如果这一系列过程立刻发生在每一个setState之后,就可能会有性能问题。
  3. 在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果。

总结

  1. 通过setState去更新this.state,不要直接操作this.state,请把它当成不可变的。
  2. 调用setState更新this.state不是马上生效的,它是异步滴,所以不要天真以为执行完setState后this.state就是最新的值了。
  3. 多个顺序执行的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};
        });
    }
}

注意点

  1. setState可能会引发不必要的渲染(renders)
  2. setState无法完全掌控应用中所有组件的状态