zoukankan      html  css  js  c++  java
  • setState详解

     

    我们都知道,React通过this.state来访问state,通过this.setState()方法来更新state。当this.setState()方法被调用的时候,React会重新调用render方法来重新渲染UI

    setState异步更新

    setState方法通过一个队列机制实现state更新,当执行setState的时候,会将需要更新的state合并之后放入状态队列,而不会立即更新this.state(可以和浏览器的事件队列类比)。如果我们不使用setState而是使用this.state.key来修改,将不会触发组件的re-render。如果将this.state赋值给一个新的对象引用,那么其他不在对象上的state将不会被放入状态队列中,当下次调用setState并对状态队列进行合并时,直接造成了state丢失。(这里特别感谢@Dcatfly的指正) 
    我们来看一下React文档中对setState的说明

        void setState(
          function|object nextState,
          [function callback]
        )

    The second (optional) parameter is a callback function that will be executed once setState is completed and the component is re-rendered. 
    翻译一下,第二个参数是一个回调函数,在setState的异步操作结束并且组件已经重新渲染的时候执行。也就是说,我们可以通过这个回调来拿到更新的state的值。 
    React也正是利用状态队列机制实现了setState的异步更新,避免频繁地重复更新state(pending的意思是未定的,即将发生的)

       //将新的state合并到状态更新队列中
       var nextState =  this._processPendingState(nextProps, nextContext);
       //根据更新队列和shouldComponent的状态来判断是否需要更新组件
       var shouldUpdate = 
          this._pendingForceUpdate ||
          !inst.shouldComponentUpdate ||
          inst.shouldComponentUpdate(nextProps, nextState, nextContext);

    setState循环调用风险

    当调用setState时,实际上会执行enqueueSetState方法,并对partialState以及_pending-StateQueue更新队列进行合并操作,最终通过enqueueUpdate执行state更新 
    而performUpdateIfNecessary方法会获取_pendingElement,_ pendingStateQueue,_ pending-ForceUpdate,并调用receiveComponent和updateComponent方法进行组件更新 
    如果在shouldComponentUpdate或者componentWillUpdate方法中调用setState,此时this._pending-StateQueue != null,就会造成循环调用,使得浏览器内存占满后崩溃

    调用栈

    既然setState最终是通过enqueueUpdate执行state更新,那么enqueueUpdate到底是如何更新state的呢? 首先看下面的问题

       import React, { Component } from 'react';
       class Example extends Component {
           constructor(){
               super();
               //在组件初始化可以直接操作this.state
               this.state = {
                   val: 0
               }
           }
           componentDidMount(){
               this.setState({
                  val: this.state.val + 1
               });
               //第一次输出
               console.log(this.state.val);
               this.setState({
                  val: this.state.val + 1
               });
               //第二次输出
               console.log(this.state.val);
               setTimeout(()=>{
                  this.setState({val: this.state.val + 1});
                   //第三次输出
                   console.log(this.state.val);
                   this.setState({
                      val: this.state.val + 1
                   });
                   //第四次输出
                   console.log(this.state.val);
               }, 0);  
           }
           render(){
               return null;
           }
       }

    上述代码中,4次console.log打印出来的val分别是: 0,0,2 ,3 
    我们来看一个简化的setState的调用栈

       this.setState(newState) =>
       newState存入pending队列 =>
       调用enqueueUpdate =>
       是否处于批量更新模式 =>
       是的话将组件保存到dirtyComponents
       不是的话遍历dirtyComponents,调用updateComponent,更新pending state or props

    enqueueUpdate的源码如下(上面流程的第三步)(batching的意思是批量的)

       function enqueueUpdate(component){
           //injected注入的
           ensureInjected();
           //如果不处于批量更新模式
           if(!batchingStrategy.isBatchingUpdates){
               batchingStrategy.batchedUpdates(enqueueUpdate, component);
               return;
           }
           //如果处于批量更新模式
           dirtyComponents.push(component);
       }

    如果isBatchingUpdates为true,则对所有队列中的更新执行batchedUpdates方法,否则只把当前组件(即调用了setState的组件)放入dirtyComponents数组中,例子中4次setState调用的表现之所以不同,这里的逻辑判断起了关键作用

    事务

    事务就是将需要执行的方法使用wrapper封装起来,再通过事务提供的perform方法执行,先执行wrapper中的initialize方法,执行完perform之后,在执行所有的close方法,一组initialize及close方法称为一个wrapper。 
    那么事务和setState方法的不同表现有什么关系,首先我们把4次setState简单归类,前两次属于一类,因为它们在同一调用栈中执行,setTimeout中的两次setState属于另一类。 
    在setState调用之前,已经处在batchedUpdates执行的事务中了。那么这次batchedUpdates方法是谁调用的呢,原来是ReactMount.js中的_renderNewRootComponent方法。也就是说,整个将React组件渲染到DOM中的过程就是处于一个大的事务中。而在componentDidMount中调用setState时,batchingStrategy的isBatchingUpdates已经被设为了true,所以两次setState的结果没有立即生效。 
    再反观setTimeout中的两次setState,因为没有前置的batchedUpdates调用,所以导致了新的state马上生效。

    引用原文:http://blog.csdn.net/sysuzhyupeng/article/details/63250741

    写博客是为了记住自己容易忘记的东西,另外也是对自己工作的总结,文章可以转载,无需版权。希望尽自己的努力,做到更好,大家一起努力进步!

    如果有什么问题,欢迎大家一起探讨,代码如有问题,欢迎各位大神指正!

  • 相关阅读:
    爬取东方财富财报
    Excel自动填充功能
    解决sqlalchemy连接数据库出现的报错
    mysql常用技巧
    Linux常用命令
    my python day7
    my python day6
    my python day5
    my python day4
    Java反射技术概述
  • 原文地址:https://www.cnblogs.com/summary-2017/p/7551576.html
Copyright © 2011-2022 走看看