美文网首页
React学习之setState的实现机制

React学习之setState的实现机制

作者: 青艹止 | 来源:发表于2019-01-03 11:21 被阅读0次

            在react中,通过管理状态来实现对组件的管理,通过this.state()来访问state,通过this.setState方法来更新state,当this.setState方法被调用时,react会重新调用render来重新渲染UI。

    一、setState的全部实现过程:

            1、enqueueSetState将state放入队列中,并调用enqueueUpdate处理要更新的Component

            2、如果组件当前正处于update事务中,则先将Component存入dirtyComponent中。否则调用batchedUpdates处理。

             3、batchedUpdates发起一次transaction.perform()事务

             4、开始执行事务初始化,运行,结束三个阶段

                初始化:事务初始化阶段没有注册方法,故无方法要执行

                运行:执行setSate时传入的callback方法,一般不会传callback参数

                结束:更新isBatchingUpdates为false,并执行FLUSH_BATCHED_UPDATES这个wrapper中的close方法

              5、FLUSH_BATCHED_UPDATES在close阶段,会循环遍历所有的dirtyComponents,调用updateComponent刷新组件,并执行它的pendingCallbacks, 也就是setState中设置的callback。

    二、setState异步更新

    源码:

    //将新的state合并到状态更新队列中

    var nextState = this._processPendingState(nextProps,nextContext);

    //根据更新队列和 shouldComponentUpdate的状态来判断是否需要更新组件

    var shouldUpdate = this._pendingForceUpdate || ! inst. shouldComponentUpdate || inst. shouldComponentUpdate(nextProps, nextState, nextContext)

            注意:如果不通过setState而直接修改this.state的值,而是诸如这样: this.state.value = 1,那么该state将不会被放入状态队列中,下次调用this.setState并对状态队列进行合并时,将会忽略之前直接别修改的state,因此我们应该用setState更新state的值。

    三、setState的循环调用

            React在setState之后,会经对state进行diff,判断是否有改变,然后去diff dom决定是否要更新UI。如果这一系列过程立刻发生在每一个setState之后,就可能会有性能问题。

            当调用setState时,实际上会执行enqueueSetState方法,并对partialState以及_pendingStateQueue更新队列进行合并,最终通过enqueueUpdate执行state更新。

            而performUpdateIfNecessary方法获取_pendingElement、_pendingStateQueue、_pendingForceUpdate,并调用 reciveComponent 和updateComponent方法进行组件更新。

            在短时间内频繁setState。React会将state的改变压入栈中,在合适的时机,批量更新state和视图,达到提高性能的效果。

            注意:setState不能在shouldComponentUpdate或componentWillUpdate中调用setState,则会造成循环调用,将浏览器的内存占满后崩溃。

    四、事务 transaction

    transaction的运行过程

            · transaction的使用场景:

            1、在一次 DOM reconciliation(调和,即 state 改变导致 Virtual DOM 改变,计算真实 DOM 该如何改变的过程)的前后,保证 input 中选中的文字范围(range)不发生变化。

            2、当 DOM 节点发生重新排列时禁用事件,以确保不会触发多余的 blur/focus 事件。同时可以确保 DOM 重拍完成后事件系统恢复启用状态。

            3、当 worker thread 的 DOM reconciliation 计算完成后,由 main thread 来更新整个 UI

    在渲染完新的内容后调用所有 componentDidUpdate 的回调

            · 事务通过wrapper进行封装。

            1、一个wrapper包含一对initialize和close方法。比如RESET_BATCHED_UPDATES

    var RESET_BATCHED_UPDATES = {

      // 初始化调用

      initialize: emptyFunction,

      // 事务执行完成,close时调用

      close: function () {

        ReactDefaultBatchingStrategy.isBatchingUpdates = false;

      }

    };

            2、transation 被包装在wrapper中,比如

    var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

            transaction是通过transaction.perform(callback, args…)方法进入的,它会先调用注册好的wrapper中的initialize方法,然后执行perform方法中的callback,最后再执行close方法。

    五、总结

            在react中,根绝setState调用栈的不同,我们可以将它划分为两类,一类是在componentDidMount中,一类是在setTimeOut中。


    这是一个例子:

    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 次 打印  0

            this.setState({val: this.state.val + 1});

            console.log(this.state.val);        // 第 2 次 打印  0   

             setTimeout(() => {

                  this.setState({val: this.state.val + 1});

                  console.log(this.state.val);      // 第 3 次 打印  2      

                  this.setState({val: this.state.val + 1});

                  console.log(this.state.val);      // 第 4 次 打印  3   

         }, 0);

      }

      render() {

        return null;

      }

    };


    代码运行过程:

    this.setState(newState) ——newState存入_pendingStateQueue —— 是否处于batch update中

    处于batch update中: component保存在dirtyComponents中  

    不处于batch update中:遍历dirtyComponents、调用updateComponent、更新state


            在componentDidMount中调用setState时,batchingStrategy的isBatchingUpdates已经被设置为true了,所以setState的结果并没有立即生效,而是被放进了dirtyComponents中。所以前两次打印this.state.val都是0,因为新的state还没被应用到组件中。

             setTimeOut中的两次setState,因为没有前置的batchedUpdate调用,所以batchingStrategy的isBatchingUpdates标志位是false,也就导致了新的state马上生效,没有走到dirtyComponents分支。也就是说,setTimeOut中的第一次执行,setState时,this.state.val为1,而setState完成后打印时this.state.val变成了2。第二次的setState同理,打印结果为3。

    相关文章

      网友评论

          本文标题:React学习之setState的实现机制

          本文链接:https://www.haomeiwen.com/subject/yiaalqtx.html