内容简介:因为
- 我们都知道,
React
通过this.state
来访问state
,通过this.setState()
方法来更新state
。当this.setState()
方法被调用的时候,React
会重新调用render
方法来重新渲染UI
- 首先如果直接在
setState
后面获取state
的值是获取不到的。在React
内部机制能检测到的地方,setState
就是异步的;在React
检测不到的地方,例如setInterval
,setTimeout
,setState
就是同步更新的
因为 setState
是可以接受两个参数的,一个 state
,一个回调函数。因此我们可以在回调函数里面获取值
-
setState
方法通过一个队列机制实现state
更新,当执行setState
的时候,会将需要更新的state
合并之后放入状态队列,而不会立即更新this.state
- 如果我们不使用
setState
而是使用this.state.key
来修改,将不会触发组件的re-render
。 - 如果将
this.state
赋值给一个新的对象引用,那么其他不在对象上的state
将不会被放入状态队列中,当下次调用setState
并对状态队列进行合并时,直接造成了state
丢失
1.1 setState批量更新的过程
在 react
生命周期和合成事件执行前后都有相应的钩子,分别是 pre
钩子和 post
钩子, pre
钩子会调用 batchedUpdate
方法将 isBatchingUpdates
变量置为 true
,开启批量更新,而 post
钩子会将 isBatchingUpdates
置为 false
-
isBatchingUpdates
变量置为true
,则会走批量更新分支,setState
的更新会被存入队列中,待同步代码执行完后,再执行队列中的state
更新。isBatchingUpdates
为true
,则把当前组件(即调用了setState
的组件)放入dirtyComponents
数组中;否则batchUpdate
所有队列中的更新 - 而在原生事件和异步操作中,不会执行
pre
钩子,或者生命周期的中的异步操作之前执行了pre
钩子,但是pos
钩子也在异步操作之前执行完了,isBatchingUpdates
必定为false
,也就不会进行批量更新
enqueueUpdate
包含了 React
避免重复 render
的逻辑。 mountComponent
和 updateComponent
方法在执行的最开始,会调用到 batchedUpdates
进行批处理更新,此时会将 isBatchingUpdates
设置为 true
,也就是将状态标记为现在正处于更新阶段了。 isBatchingUpdates
为 true
,则把当前组件(即调用了 setState
的组件)放入 dirtyComponents
数组中;否则 batchUpdate
所有队列中的更新
1.2 为什么直接修改this.state无效
- 要知道
setState
本质是通过一个队列机制实现state
更新的。 执行setState
时,会将需要更新的state合并后放入状态队列,而不会立刻更新state
,队列机制可以批量更新state
。 - 如果不通过
setState
而直接修改this.state
,那么这个state
不会放入状态队列中,下次调用setState
时对状态队列进行合并时,会忽略之前直接被修改的state
,这样我们就无法合并了,而且实际也没有把你想要的state
更新上去
1.3 什么是批量更新 Batch Update
在一些 mv*
框架中,,就是将一段时间内对 model
的修改批量更新到 view
的机制。比如那前端比较火的 React
、 vue
( nextTick
机制,视图的更新以及实现)
1.4 setState之后发生的事情
-
setState
操作并不保证是同步的,也可以认为是异步的 -
React
在setState
之后,会经对state
进行diff
,判断是否有改变,然后去diff dom
决定是否要更新UI
。如果这一系列过程立刻发生在每一个setState
之后,就可能会有性能问题 - 在短时间内频繁
setState
。React
会将state
的改变压入栈中,在合适的时机,批量更新state
和视图,达到提高性能的效果
1.5 如何知道state已经被更新
传入回调函数
setState({ index: 1 }}, function(){ console.log(this.state.index); })
在钩子函数中体现
componentDidUpdate(){ console.log(this.state.index); }
二、setState循环调用风险
- 当调用
setState
时,实际上会执行enqueueSetState
方法,并对partialState
以及_pending-StateQueue
更新队列进行合并操作,最终通过enqueueUpdate
执行state
更新 - 而
performUpdateIfNecessary
方法会获取_pendingElement
,_pendingStateQueue
,_pending-ForceUpdate
,并调用receiveComponent
和updateComponent
方法进行组件更新 - 如果在
shouldComponentUpdate
或者componentWillUpdate
方法中调用setState
,此时this._pending-StateQueue != null
,就会造成循环调用,使得浏览器内存占满后崩溃
三、事务
- 事务就是将需要执行的方法使用
wrapper
封装起来,再通过事务提供的perform
方法执行,先执行wrapper
中的initialize
方法,执行完perform
之后,在执行所有的close
方法,一组initialize
及close
方法称为一个wrapper
。 - 那么事务和
setState
方法的不同表现有什么关系,首先我们把4
次setStat
e简单归类,前两次属于一类,因为它们在同一调用栈中执行,setTimeout
中的两次setState
属于另一类 - 在
setState
调用之前,已经处在batchedUpdates
执行的事务中了。那么这次batchedUpdates
方法是谁调用的呢,原来是ReactMount.js
中的_renderNewRootComponent
方法。也就是说,整个将React
组件渲染到DOM
中的过程就是处于一个大的事务中。而在componentDidMount
中调用setState
时,batchingStrategy
的isBatchingUpdates
已经被设为了true
,所以两次setState
的结果没有立即生效 - 再反观
setTimeout
中的两次setState
,因为没有前置的batchedUpdates
调用,所以导致了新的state
马上生效
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 深度分析ConcurrentHashMap原理分析
- Spring源码分析:@Autowired注解原理分析
- Struts2 源码分析-----工作原理分析
- KVO实现原理分析
- JavaScript运行原理分析
- LevelDB原理分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。