www.9778.com 12

www.9778.com:05HW_MeiTuan_TableView_InsertAdd_MoreCustomStyleCell_OpenClose

Spring Framework 5.1.11 发布了,更新内容如下:

大纲

一、多个自定义单元格
步骤:
1.自定义类型
2.Cell重用标识符
3.Cell上控件的关联
二、tableView的展开与闭合
步骤:
1.将被点击的区头bool取反
2.更新区中的行数
2.1 方法1(效率低):reloadData:更新表中所有的数据
2.2 方法2(效率高):reloadSections:刷新某个区的数据
3.根据bool值,刷新相应的区。
4.处理图片旋转
刷新表时,自定义区头方法会再次被执行,因此,可以在此方法中设置图片的旋转

图片旋转:
自定义区头方法中处理

 tipImgView.transform

小提示:C语言中的bool数组

    BOOL _flagArray[5];//[no,no,no,no,no];

三、删除
步骤:
1.点击按钮,让表进入编辑状态
2.设置编辑风格(如果是删除,不需要设置)
3.提交编辑
3.1 删除数据源
3.2 创建单元格索引
3.3 存到数组中


www.9778.com 1

新特性

  • 竞争条件会影响 AbstractJaxb2HttpMessageConverter 中的性能
    – JAXBContext
    creation #23894
  • 支持 WebFlux
    编解码器中对输入流处理的限制 #23885
  • 允许在 BeanDefinitionBuilder
    上设置主标志 #23808
  • ResponseStatusException
    的处理也包括响应头的设置 #23800

正文

一、多个自定义单元格
步骤:
1.类型
2.重用标识符
3.关联
项目:TableView_MoreCustomStyleCell0406
1.1

2.1

2.2

3.1

3.2
重点:

3.3

二、tableView的展开与闭合
项目:TableView_OpenCloseDelete0406
步骤:
1.将被点击的区头bool取反
2.更新区中的行数
2.1 方法1(效率低):reloadData:更新表中所有的数据
2.2 方法2(效率高):reloadSections:刷新某个区的数据
3.根据bool值,刷新相应的区。
4.处理图片旋转
刷新表时,自定义区头方法会再次被执行,因此,可以在此方法中设置图片的旋转

图片旋转:
在自定义区头方法中处理

 tipImgView.transform

小提示:C语言中的bool数组

    BOOL _flagArray[5];//[no,no,no,no,no];

源码:

#pragma mark - 区头按钮点击事件 展开/闭合
    //1.将被点击的区头bool取反
    _flagArray[sender.tag] = !_flagArray[sender.tag];
    //2.更新区中的行数
    //2.1 方法1(效率低):reloadData:更新表中所有的数据
//    [_tableView reloadData];
    //2.2 方法2(效率高):reloadSections:刷新某个区的数据
    //将刷新区的索引放入索引集合
    //3.根据bool值,刷新相应的区。
        NSArray *array = [_totalArr objectAtIndex:section];
        return array.count;

刷新顺序:
设置每个区的行数section4
设置每个区的行数section0
设置每个区的行数section1
设置每个区的行数section2
设置每个区的行数section3
设置单元格内容(0,2)
设置单元格内容(0,3)
设置单元格内容(0,4)
设置单元格内容(1,0)
设置单元格内容(1,1)
设置单元格内容(1,2)
设置单元格内容(1,3)
设置单元格内容(1,4)
设置单元格内容(2,0)
设置单元格内容(2,1)
自定义区头section0
自定义区头section1
自定义区头section2

www.9778.com:05HW_MeiTuan_TableView_InsertAdd_MoreCustomStyleCell_OpenClose。处理图片旋转

//4.处理图片旋转
    //刷新表时,自定义区头方法会再次被执行,因此,可以在此方法中设置图片的旋转
    //4.1 旋转90度
    if (_flagArray[section] == YES)
    {
        tipImgView.transform = CGAffineTransformMakeRotation(M_PI_2);
    }
    //4.2 恢复
    else
    {
        tipImgView.transform = CGAffineTransformIdentity;
    }

三、删除
步骤:
1.点击按钮,让表进入编辑状态
2.设置编辑风格(如果是删除,不需要设置)
3.提交编辑
3.1 删除数据源
3.2 创建单元格索引
3.3 存到数组中

#pragma mark - 单元格的删除
//1.点击按钮,让表进入编辑状态
- (void)deleteClick:(UIBarButtonItem *)item
{
    [_tableView setEditing:!_tableView.editing animated:YES];
    item.title = _tableView.editing == NO ? @"删除" : @"完成";
}
//2.设置编辑风格(如果是删除,不需要设置)
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return UITableViewCellEditingStyleDelete;
}
//3.提交编辑
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
    //1.删除数据源
    NSMutableArray *mArray = [_totalArr objectAtIndex:indexPath.section];
    [mArray removeObjectAtIndex:indexPath.row];
    //2.创建单元格索引
    NSIndexPath *cellIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
    //3.存到数组中
    NSArray *array = [NSArray arrayWithObjects:cellIndexPath, nil];
    //IndexPaths:存放索引的数组
    //删除单元格时,系统会刷新表
    [tableView deleteRowsAtIndexPaths:array withRowAnimation:UITableViewRowAnimationFade];
}

layout: docs-default

React基础2–深入挖掘setState

Bug 修复

  • 在 registrar
    中重新排序日期格式转换器 #23896
  • MockServletContext 应该将 InvalidPathException 视为
    IOException #23888
  • 在 MockCookie
    中保留过期属性 #23881

详情可查看更新说明。

(文/开源中国)    

Clients

Client类是OpenID
Connect或者OAuth2客户端的一个抽象–比如:原生应用,网站应用,基于JS的前端应用(链接).

  • Enabled

    • 标明这个客户端是否启用,默认为true.
  • ClientId

    • 唯一的客户编号
  • ClientSecrets

    • 客户端密钥列表—只有在需要密钥的处理流(flow)中使用。
  • ClientName

    • 客户端显示名(用于授权页面和日志服务)
  • ClientUri

    • 关于客户端的详细信息网页 (在授权页面上使用)
  • LogoUri

    • 客户端的Logo(在授权页面上使用)
  • RequireConsent

    • 指定是否需要用户明确授权,默认为 true.
  • AllowRememberConsent

    • 指定是否可以记录用户授权决定。默认为true.
  • Flow

    • www.9778.com,指定客户端接受的处理流程(flow) (AuthorizationCode, Implicit,
      Hybrid, ResourceOwner, ClientCredentials 或者 Custom).
      默认为Implicit.
  • AllowClientCredentialsOnly

    • 获取或者设置
      是否只允许客户端使用客户端凭据请求令牌。如果希望客户端可以使用用户为中心的处理流程(flow)和附加的客户端凭据处理流程(flow).默认为false.
      应该只用于可信的客户端(如:非隐式流程).
  • RedirectUris

    • 指定可以接受令牌和授权码的重定向URIs。
  • PostLogoutRedirectUris

    • 指定登出时可以重定向的URIs
  • LogoutUri (added in v2.2)

    • 指定客户端基于HTTP的登出URI
  • LogoutSessionRequired (added in v2.2)

    • 指定是否把用户的会话号附加到登出Uri上面。默认为true。
  • RequireSignOutPrompt (added in v2.4)

    • 指定登出时是否显示确认页面让用户确认登出。默认为false。
  • AllowedScopes

    • 默认客户端不可以访问任何作用域–需要指明客户端可以访问的作用域(推荐)。或者也可以设置AllowAccessToAllScopes为true,让允许访问全部作用域.
  • AllowAccessTokensViaBrowser ( v2.5新增)

    • 指定客户端是否可以通过浏览器请求访问令牌。
      这个可以强化多返回类型的处理流程(比如:
      禁止混合处理流程客户端(应该只使用code id_token)使用token响应类型防止令牌泄露到浏览器。限制它只能使用code id_token)
  • AllowedCustomGrantTypes

    • 当使用Custom处理流程,需要同时指定这个客户端可以使用哪一种自定义授权类型。在这里显式指定授权类型(推荐)或者设置
      AllowAccessToAllCustomGrantTypes 为true.
  • IdentityTokenLifetime

    • 身份令牌的生存期,单位是秒。(默认300秒/5分钟)
  • AccessTokenLifetime

    • 访问令牌的生存期,单位是秒。(默认3600秒/1小时)
  • AuthorizationCodeLifetime

    • 授权码的生存期,单位是秒。(默认300秒/5分钟)
  • AbsoluteRefreshTokenLifetime

    • 更新令牌的最大生存期,单位是秒。(默认2592000秒/30天)
  • SlidingRefreshTokenLifetime

    • 更新令牌的延展生存期,默认是1296000秒 / 15 天
  • RefreshTokenUsage

    • ReUse: 使用更新令牌刷新令牌的时候,原更新令牌保持不变。
    • OneTime: 更新令牌每次使用后都会改变。
  • RefreshTokenExpiration

    • Absolute:
      更新令牌会在固定时间过期(在AbsoluteRefreshTokenLifetime属性中指定)
    • Sliding:
      当刷新令牌的时候,更新令牌的生存期会延展(延展长度在SlidingRefreshTokenLifetime指定).延展后生存期不能超过
      AbsoluteRefreshTokenLifetime.
  • UpdateAccessTokenClaimsOnRefresh

    • 获取或设置访问令牌(及所带声明)是否会在使用刷新令牌时更新。
  • AccessTokenType

    • 指定访问令牌是一个参考令牌还是自包含的JWT(JSON Web Token)
      令牌(默认是Jwt).
  • EnableLocalLogin

    • 指定客户端可以使用本地账号,还是只能使用第三方的IdPs.
      默认是true.
  • IdentityProviderRestrictions

    • 制定哪一个外部 IdPs可以在这个客户端中使用(空表示所有IdPs都允许).
      默认为空.
  • IncludeJwtId

    • 指定JWT访问令牌是否内嵌唯一标识(通过jti 声明).
  • AllowedCorsOrigins

    • 如果指定,将使用默认的CORS策略服务(内存服务和EF)来为javascript客户端生成跨域策略。
  • Claims

    • 为这个客户端设置声明 (将包含在访问令牌里).
  • AlwaysSendClientClaims

    • 如果设置,每次处理流程都会发送声明。如果没有设置,那么只有客户端凭据处理流程发送声明。(默认是
      false)
  • PrefixClientClaims

    • 如果设置,所有客户端声明会加上client_来确保不和用户声明冲突,默认是true.

还有一些关于更新令牌的设置,请看这里

众所周知,react通过this.state来访问,通过setState()方法来更新state。当this.setState()被调用的时候react会调用render重新渲染。
当执行setState的时候,会将需要更新的state合并到状态队列中去,这样可以高效的批量更新state。假如直接更改this.state的值那么state将不会被放到状态队列中去,当合并state队列的时候则忽略之前被修改的state,造成无法预知的错误。
首先,我们看一段setState源码

例子:为隐式处理流程配置客户端

var client = new Client
{
    ClientName = "JS Client",
    Enabled = true,

    ClientId = "implicitclient",
    Flow = Flows.Implicit,

    RequireConsent = true,
    AllowRememberConsent = true,

    RedirectUris = new List<string>
    {
        "https://myapp/callback.html",
    },

    PostLogoutRedirectUris = new List<string>
    {
        "http://localhost:23453/index.html",
    }
}
// 将setState事务放入队列中
ReactComponent.prototype.setState = function (partialState, callback) {
  !(typeof partialState === 'object' || typeof partialState === 'function' || partialState == null) ? process.env.NODE_ENV !== 'production' ? invariant(false, 'setState(...): takes an object of state variables to update or a function which returns an object of state variables.') : _prodInvariant('85') : void 0;
  this.updater.enqueueSetState(this, partialState);
  if (callback) {
    this.updater.enqueueCallback(this, callback, 'setState');
  }
};

例子:为客户端配置资源所有者的处理流程

var client = new Client
{
    ClientName = "Legacy Client",
    Enabled = true,

    ClientId = "legacy",
    ClientSecrets = new List<Secret>
    {
        new Secret("4C701024-0770-4794-B93D-52B5EB6487A0".Sha256())
    },

    Flow = Flows.ResourceOwner,

    AbsoluteRefreshTokenLifetime = 86400,
    SlidingRefreshTokenLifetime = 43200,
    RefreshTokenUsage = TokenUsage.OneTimeOnly,
    RefreshTokenExpiration = TokenExpiration.Sliding
}

// 合并队列

enqueueSetState: function (publicInstance, partialState) {
    // 获取ReactComponent组件对象
    // publicInstance 指向 ReactComponent
    var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState');

    if (!internalInstance) {
      return;
    }
    // 合并队列
    // 如果_pendingStateQueue为空,则创建它。可以发现队列是数组形式实现的
    var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = []);
    // 此处partialState 既是你传入的参数
    queue.push(partialState);
    // 将要更新的ReactComponent放入数组中
    enqueueUpdate(internalInstance);
  }

getInternalInstanceReadyForUpdate()

function getInternalInstanceReadyForUpdate(publicInstance, callerName) {
  var internalInstance = ReactInstanceMap.get(publicInstance);
  // 从map取出ReactComponent组件,
  //mountComponent时把ReactElement作为key,将ReactComponent存入了map中,
  //ReactComponent是React组件的核心,包含各种状态,数据和操作方法。而ReactElement则仅仅是一个数据类。
  if (!internalInstance) {
    if (process.env.NODE_ENV !== 'production') {
      var ctor = publicInstance.constructor;
    }
    return null;
  }
  return internalInstance;
}

enqueueUpdate()

function enqueueUpdate(component) {
  ensureInjected();

  // 如果不是正处于创建或更新组件阶段,则处理update事务
  if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

  // 如果正在创建或更新组件,则暂且先不处理update,只是将组件放在dirtyComponents数组中
  dirtyComponents.push(component);
}

batchedUpdates: function (callback, a, b, c, d, e) {
  var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
  // 批处理最开始时,将isBatchingUpdates设为true,表明正在更新
  ReactDefaultBatchingStrategy.isBatchingUpdates = true;

  // The code is written this way to avoid extra allocations
  if (alreadyBatchingUpdates) {
    callback(a, b, c, d, e);
  } else {
    // 以事务的方式处理updates,后面详细分析transaction
    transaction.perform(callback, null, a, b, c, d, e);
  }
}

var RESET_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: function () {
    // 事务批更新处理结束时,将isBatchingUpdates设为了false
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }
};
var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

源码分析(我们看看从调用setState到组建更新发生了什么):
当我们调用setState方法时,实际上是执行enqueueSetState方法。
enqueueSetState方法接受两个参数,一个this指向ReactComponent组件对象,一个是我们设置的参数。
enqueueSetState方法接受参数后,会调用getInternalInstanceReadyForUpdate方法,该方法接受ReactComponent组件对象,然后从map中取出ReactComponent组件并返回。
如果返回不为空,则合并队列,如果_pendingStateQueue为空,则创建它,然后将我们设置的参数合并到一起。
最后调用enqueueUpdate方法。将更新的ReactComponent组件作为参数。
enqueueUpdate方法会先判断正否处于创建或更新组件阶段,如果是则暂且先不处理update,只是将组件放在dirtyComponents数组中。否则,将调用batchedUpdates方法。
enqueueUpdate包含了React避免重复render的逻辑。mountComponent和updateComponent方法在执行的最开始,会调用到batchedUpdates进行批处理更新,此时会将isBatchingUpdates设置为true,也就是将状态标记为现在正处于更新阶段了。之后React以事务的方式处理组件update,事务处理完后会调用wrapper.close(),
而TRANSACTION_WRAPPERS中包含了RESET_BATCHED_UPDATES这个wrapper,故最终会调用RESET_BATCHED_UPDATES.close(),
它最终会将isBatchingUpdates设置为false。
到此,我相信认真读的童鞋,肯定有个疑问。。。
那就是transaction.perform(callback, null, a, b, c, d,
e);里面的callback就是enqueueUpdate;
那么这样不就进入到死循环了么,怎么还调用updateComponent更新组件呢?带着疑问往下看。。

perform: function (method, scope, a, b, c, d, e, f) {
    var errorThrown;
    var ret;
    try {
      this._isInTransaction = true;
      errorThrown = true;
      // 先运行所有wrapper中的initialize方法
      this.initializeAll(0);

      // 再执行perform方法传入的callback
      ret = method.call(scope, a, b, c, d, e, f);
      errorThrown = false;
    } finally {
      try {
        if (errorThrown) {
          // 最后运行wrapper中的close方法
          try {
            this.closeAll(0);
          } catch (err) {}
        } else {
          // 最后运行wrapper中的close方法
          this.closeAll(0);
        }
      } finally {
        this._isInTransaction = false;
      }
    }
    return ret;
  },

  initializeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有注册的wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      try {
        this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
        // 调用wrapper的initialize方法
        this.wrapperInitData[i] = wrapper.initialize ? wrapper.initialize.call(this) : null;
      } finally {
        if (this.wrapperInitData[i] === Transaction.OBSERVED_ERROR) {
          try {
            this.initializeAll(i + 1);
          } catch (err) {}
        }
      }
    }
  },

  closeAll: function (startIndex) {
    var transactionWrappers = this.transactionWrappers;
    // 遍历所有wrapper
    for (var i = startIndex; i < transactionWrappers.length; i++) {
      var wrapper = transactionWrappers[i];
      var initData = this.wrapperInitData[i];
      var errorThrown;
      try {
        errorThrown = true;
        if (initData !== Transaction.OBSERVED_ERROR && wrapper.close) {
          // 调用wrapper的close方法,如果有的话
          wrapper.close.call(this, initData);
        }
        errorThrown = false;
      } finally {
        if (errorThrown) {
          try {
            this.closeAll(i + 1);
          } catch (e) {}
        }
      }
    }
    this.wrapperInitData.length = 0;
  }

通过transaction.perform的源码我们可以知道,perform先运行wrapper中的initialize方法,再执行perform方法传入的callback,
最后运行wrapper中的close方法。而在最后

var TRANSACTION_WRAPPERS = [FLUSH_BATCHED_UPDATES, RESET_BATCHED_UPDATES];

wrapper中注册了两个wrapper:
FLUSH_BATCHED_UPDATES

var FLUSH_BATCHED_UPDATES = {
  initialize: emptyFunction,
  close: ReactUpdates.flushBatchedUpdates.bind(ReactUpdates)
};

flushBatchedUpdates ()

var flushBatchedUpdates = function () {
  // 循环遍历处理完所有dirtyComponents
  while (dirtyComponents.length || asapEnqueued) {
    if (dirtyComponents.length) {
      var transaction = ReactUpdatesFlushTransaction.getPooled();
      // close前执行完runBatchedUpdates方法,这是关键
      transaction.perform(runBatchedUpdates, null, transaction);
      ReactUpdatesFlushTransaction.release(transaction);
    }
    if (asapEnqueued) {
      asapEnqueued = false;
      var queue = asapCallbackQueue;
      asapCallbackQueue = CallbackQueue.getPooled();
      queue.notifyAll();
      CallbackQueue.release(queue);
    }
  }
};

function runBatchedUpdates(transaction) {
  var len = transaction.dirtyComponentsLength;
  dirtyComponents.sort(mountOrderComparator);

  for (var i = 0; i < len; i++) {
    // dirtyComponents中取出一个component
    var component = dirtyComponents[i];

    // 取出dirtyComponent中的未执行的callback,下面就准备执行它了
    var callbacks = component._pendingCallbacks;
    component._pendingCallbacks = null;

    var markerName;
    if (ReactFeatureFlags.logTopLevelRenders) {
      var namedComponent = component;
      if (component._currentElement.props === component._renderedComponent._currentElement) {
        namedComponent = component._renderedComponent;
      }
    }
    // 执行updateComponent
    ReactReconciler.performUpdateIfNecessary(component, transaction.reconcileTransaction);

    // 执行dirtyComponent中之前未执行的callback
    if (callbacks) {
      for (var j = 0; j < callbacks.length; j++) {
        transaction.callbackQueue.enqueue(callbacks[j], component.getPublicInstance());
      }
    }
  }
}

performUpdateIfNecessary()

performUpdateIfNecessary: function (transaction) {
    if (this._pendingElement != null) {
      // receiveComponent会最终调用到updateComponent,从而刷新View
      ReactReconciler.receiveComponent(this, this._pendingElement, transaction, this._context);
    }

    if (this._pendingStateQueue !== null || this._pendingForceUpdate) {
      // 执行updateComponent,从而刷新View。这个流程在React生命周期中讲解过
      this.updateComponent(transaction, this._currentElement, this._currentElement, this._context, this._context);
    }
  },

到这一步是不是看到了receiveComponent 和
updateComponent有点欣喜若狂呢。
flushBatchedUpdates
方法会循环遍历处理完所有dirtyComponents,在close前执行完runBatchedUpdates方法。
runBatchedUpdates方法调用performUpdateIfNecessary();
最后进入update阶段。

setState循环调用风险

当调用setState时,实际上会执行enqueueSetState方法,对_pendingStateQueue合并,然后执行enqueueUpdate更新state。假如在componentWillUpdate调用setState此时,this._pendingStateQueue
!= null
所以,performUpdateIfNecessary方法会调用updateComponent方法更新组件,一次造成循环调用。

setState封装到异步函数里为什么就变成了同步的?

我们理一下setState的过程:
首先:enqueueUpdate方法
在该方法中通过判断batchingStrategy.isBatchingUpdates的值来决定如何处理。
我们在此处打印一下batchingStrategy.isBatchingUpdates

www.9778.com 2

Paste_Image.png

www.9778.com 3

Paste_Image.png

www.9778.com 4

Paste_Image.png

将flushBatchedUpdates

www.9778.com 5

Paste_Image.png

www.9778.com 6

Paste_Image.png

www.9778.com 7

Paste_Image.png

最后一步我们打印updateComponent里面的state更新后的state

www.9778.com 8

Paste_Image.png

假如我们在componentDidMount里面设置setState

 componentDidMount(){
       console.log('父组件didmount')
       this.setState({num:this.state.num+2})
       console.log(this.state.num)
       console.log('ok')
    }

结果为:

www.9778.com 9

Paste_Image.png

在enqueueUpdate方法里面batchingStrategy.isBatchingUpdates为true。代表正在穿件组件或更新,此时:
暂且先不处理update,只是将组件放在dirtyComponents数组中
dirtyComponents.push(component);
然后继续执行创建或更新组件。直到执行完didcomponent。
此时,打印this.state还是之前的,因为还没完成更新。
继续往下执行,直到updateComponent。在updateComponent里面
执行

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

state正式更新。
假如:
我们将setState封装到setTimeout函数里:

componentDidMount(){
       console.log('父组件didmount')
       setTimeout(()=>{
         console.log('start')
         this.setState({num:this.state.num+2})
         console.log(this.state.num)
       })
       console.log(this.state.num)
       console.log('ok')
    }

执行结果:

www.9778.com 10

Paste_Image.png

因为setTimeout是异步的,所以componentDidMount会在继续执行,直到结束。此时
会执行ReactDefaultBatchingStrategy.isBatchingUpdates = false;
代表已经创建结束。
因此,在执行setState的时候batchingStrategy.isBatchingUpdates = false.
所以将执行

if (!batchingStrategy.isBatchingUpdates) {
    batchingStrategy.batchedUpdates(enqueueUpdate, component);
    return;
  }

操作,进而执行

www.9778.com 11

Paste_Image.png

并且isBatchingUpdates被设置成 true,将组建设置成更新状态。
这时候react重新调用enqueueUpdate方法。然后继续执行后续操作。

将setState放到方法里执行

   sets(){
        this.setState({
            value : true
        })
        console.log(this.setState.value)
    }
    render(){
        return(
            <div>

                <button onClick={this.sets.bind(this)}>切换</button>
            </div>
        )
    }

执行结果,点击切换后:

www.9778.com 12

Paste_Image.png

我们可以看到在最开始,无论是在周期函数,还是点击时间,都会调用
batchedUpdates方法;

batchedUpdates: function (callback, a, b, c, d, e) {
    var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
    console.log('batchedUpdates')
    ReactDefaultBatchingStrategy.isBatchingUpdates = true;
    if (alreadyBatchingUpdates) {
      callback(a, b, c, d, e);
    } else {
      transaction.perform(callback, null, a, b, c, d, e);
    }
  }

设置 ReactDefaultBatchingStrategy.isBatchingUpdates =
true;代表组件处于更新状态。
在结束都会调用

 close: function () {
    console.log('isBatchingUpdates = false')
    ReactDefaultBatchingStrategy.isBatchingUpdates = false;
  }

设置 ReactDefaultBatchingStrategy.isBatchingUpdates =
true.代表组件处于更新状态。
而在设置state的过程中,检测到组件处于更新状态或新建状态,则暂且先不处理update,只是将组件放在dirtyComponents数组中。所以要等到组件更新完成后在进行更新state,所以setState看起来像是异步的。但是将setState放到异步函数里面这时,组件状态已经处于完成更新或新建状态,所以setState不用等了直接就可以处理,所以像是同步的。
其他文章:
React基础1–生命周期详解
React基础3–diff算法