博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
React setState流程解析
阅读量:5991 次
发布时间:2019-06-20

本文共 4212 字,大约阅读时间需要 14 分钟。

一、setState使用

接触react框架不久,却在项目当中发现,非受控组件其更新时机的触发方式——setState,是一个异步的过程

下面是一个例子:

handelTabChange (tabName) {  this.setState({    tab: tabName  })  this.updateTabPane()}updateTabPane () {  const { tab } = this.state  console.log( tab ) // not the latest one}

此时tab发生变化的时候,输出的却依然是上一个tab的名称,因此可以判断updateTabPane是在setState之前执行了。

那么,为什么setState需要异步去改变组件的state呢?

React组件是靠单向数据流构建页面dom的,除开props,自身的state改变也是引起组件渲染的主要因素,为了节省性能消耗,react有一套自己的state更新策略,为的是减少state更新对页面渲染的消耗,而这套更新策略的入口就是setState方法。

这同样也解释了为什么直接对this.state进行赋值操作并不能改变页面的渲染结果。

那么在异步的过程中,setState究竟做了哪些事儿呢?

二、 setState更新组件state

总体归纳一下setState更新组件的流程,调用setState后,会把我们想要更新的state压进一个待更新队列(即内部实例的_pendingStateQueue),然后执行入栈更新操作enqueueUpdate,判定是否处于批量更新状态。如果正在进行组件的批量更新,那么久先把实例推进dirtyComponents里面等待下一次批量更新;相反若没有批量更新在执行,则会调用一个批量更新的事务。

/** * setState源码   **/ReactComponent.prototype.setState = function (partialState, callback) {  this.updater.enqueueSetState(this, partialState); // 传入新state进队列  if (callback) { // 推入callback队列    this.updater.enqueueCallback(this, callback, 'setState')  }}enqueueSetState: function(publicInstance, partialState) {  // 获取内部实例  var internalInstance = getInternalInstanceReadyForUpdate(publicInstance, 'setState')  if (!internalInstance) {    return  }  // 更新队列合并操作  var queue = internalInstance._pendingStateQueue || (internalInstance._pendingStateQueue = [])  queue.push(partialState)  // 更新代码  enqueueUpdate(internalInstance)}

setState把我们希望更新的partialState推入待更新队列之后,就撒手交给enqueueUpdate去处理更新的时机了,我们看一下enqueueUpdate又为我们做了什么

/** * enqueueUpdate源码   **/function enqueueUpdate(component) {  ensureInjected()  // 如果不处于批量更新模式  if (!batchingStrategy.isBatchingUpdates) {    batchingStrategy.batchedUpdates(enqueueUpdate, component)    return  }  // 如果处于批量更新模式,则将该组件保存在 dirtyComponents 中  dirtyComponents.push(component)}

可以看到enqueueUpdate当中出现了一个重要的对象batchingStrategy,他有一个属性isBatchingUpdates用来告诉enqueueUpdate是应该更新,还是应该等待,把组件推入dirtyComponents里。可以想象这是一个react内部,用于控制批量更新的对象,让我们更近距离的了解它

/** * batchingStrategy源码   **/var ReactDefaultBatchingStrategy = {  isBatchingUpdates: false,  batchedUpdate: function(callback, a, b, c, d, e) {    var alreadyBatchingStrategy = ReactDefaultBatchingStrategy. isBatchingUpdates    ReactDefaultBatchingStrategy. isBatchingUpdates = true    if (alreadyBatchingStrategy) {      callback(a, b, c, d, e)    } else {      transaction.perform(callback, null, a, b, c, d, e)    }  }}

dirtyComponents当中提供的batchedUpdates其实就是我们一直寻找的,真实用来更新我们组件的方法。然而走到这一步,react却又向我们抛出了一个重大的概念——事务batchedUpdates当中transaction.perform就是事务的调用

三、 事务与componentDidMount

了解了setState执行的全过程,我们也清楚了这个函数其实并不一定是异步去执行的,倘若没有在进行更新dom时,它还是会立即触发dom的更新

//this.state.val = 0setTimeout(() => {  this.setState({val: this.state})  console.log(this.state.val) // 1  this.setState({val: this.state})  console.log(this.state.val) // 2}, 0)

当他异步的时候,从setState的源码中我们也看到了,如果想要在新的state更新后触发操作,只需要在setState的第二个参数当中传入你想要执行的回调即可。

但是,如果想要解释以下现象,我们还需要向大家介绍事务的概念。

componentDidMount () {  this.setState({ val: this.state.val + 1 })  console.log(this.state.val) // 0  this.setState({ val: this.state.val + 1 })  console.log(this.state.val) // 0 }

按照刚才的想法,当componentDidMount执行的时候,按理说页面上已经有完整的dom渲染结束了,为什么此时我调用setState不能像setTimeout里一样,立即执行对state的更新呢?下面先抛出事务的简介。

简单来说,事务是一种react的处理机制,通过使用wrapper包裹你实际想要调用的方法,做一些前置initialize)和收尾close)的工作,所以在事务包裹的方法内,会优先触发前置钩子,以及执行完后会有收尾方法调用,这在react用作异常处理使用。

所以此时不难想到,其实componentDidMountreact挂载dom节点的事务的收尾工作,在这个环节操作state会被阻塞,直到事务完全执行完毕后,才会重新调用更新。

四、 setState与生命周期

setState会触发组件的更新,同时在组件生命周期的钩子函数中我们往往会有对state的操作,操作不当很有可能发生state change =》 update =》 change state =》 state change……的死循环,那么哪些钩子函数内使用setState是安全的呢。

我们把生命周期钩子函数罗列出来

constructor -> componentWillMount -> render -> componentDidMount -> componentWillReceiveProps -> shouldComponentUpdate -> componentWillUpdate -> render -> componentDidUpdate

当中constructor中本身就有state的声明,在这里是最初的state创建,因此不需要使用setState

componentWillMount中,如果进行同步setState调用,此时的操作其实和constructor内定义state是一样的,并不会触发组件的额外渲染,当然这里可以做异步的setState操作,获取页面的初始数据。

rendershouldComponentUpdatecomponentWillUpdate这三个函数中,组件还没有渲染结束就继续调用setState,会无限触发更新,陷入死循环,是要注意的。

因此我们可用setState的生命周期钩子函数有:componentWillMountcomponentDidMountcomponentWillReceivePropscomponentDidUpdate


至此setState的原理和使用就介绍完了,但是真正使用的契机却往往是前端开发者需要去琢磨的,对于非控制组件,这是react中必要掌握的技术基础了

转载地址:http://jpnlx.baihongyu.com/

你可能感兴趣的文章
Android中如何查看内存(上)
查看>>
安全cookie登录状态设计方案
查看>>
ADO.NET
查看>>
GO 时间相关函数
查看>>
Tomcat报异常:Too many open files 的解决之路
查看>>
couchdb配置详解
查看>>
总结大中小型项目的git流程
查看>>
z-index作用于position为非static的元素上
查看>>
信息安全原理与实践(第2版)
查看>>
tcpdump的基本使用
查看>>
开源 免费 java CMS - FreeCMS-数据对象-info
查看>>
开源 java CMS - FreeCMS2.8 依申请公开
查看>>
JS判断浏览器 IE7,IE6,Mozilla
查看>>
python 下载指定网页上得图片
查看>>
Vue2 无限级分类(添加,删除,修改)
查看>>
JavaScript 获取元素的css属性
查看>>
php计算字节数(含中文)
查看>>
开源模块 Openerp Web PDF Report Preview & Print 简介 ...
查看>>
YinXiangMa_SDK_For_DotNet_V2.0接口开发者使用说明
查看>>
Spring配置事务五种方式
查看>>