react usestate更新对象,react usestate 数据更新不及时
作为react的重要组成部分,setState将组件状态的更改排队,并通知React该组件及其子组件需要用更新后的状态重新呈现。下面这篇文章带你了解一下React中的setState机制,希望对你有所帮助!
是状态反应中的一个重要概念。我们知道,React通过状态管理来管理组件。那么,React是如何控制组件的状态的,又是如何利用状态来管理组件的呢?【相关推荐:Redis视频教程】
众所周知,React通过this.state访问状态,通过this.setState()方法更新状态。当调用this.setState()时,React将再次调用render方法来重新呈现UI。
SetState已经是我们非常熟悉的API了,但是你真的了解它吗?下面我们一起来解密setState的更新机制。
setState异步更新
人们刚开始写React的时候,一般会这样写代码. state.value=1,这是完全错误的。
SetState通过队列机制更新状态。在执行setState时,需要更新的状态将被合并并放入state pair列,而不是立即更新this.state。队列机制可以高效地批量更新状态。如果不使用setState直接修改this.state的值,则该状态不会被放入状态队列中。下次调用setState并合并状态队列时,之前修改的状态将被忽略,从而导致不可预知的错误。
所以应该使用setState方法更新状态,同时React利用状态队列机制实现setState的异步更新,避免状态的频繁重复更新。相关代码如下:
//将新状态合并到状态更新队列中
var nextState=this。_processPendingState(nextProps,next context);
//根据更新队列和shouldComponentUpdate的状态确定是否需要更新组件。
var shouldUpdate=this_pendingForceUpdte !inst . shouldcomponentupdate inst . shouldcomponentupdate(next props,nextState,nextContext0
setState循环调用风险
调用setState时,实际执行enqueueSetState方法,合并partialState和_pendingStateQueue的更新队列,最后一个操作enqueueSetState执行状态更新。
而performUpdateIfNecessary方法获取_pendingElement、_pendingStateQueue和_pendingForceUpdate,并调用receiveComponent和updateComponent方法更新组件。
如果在shouldComponetUpdate或componentWillUpdate方法中调用setState,则此。_pendingStateQueue!=null,则performUpateIfNecessary方法将调用updateComponent方法来更新组件,但updateComponent方法将调用shouldComponentUpdate和componentWillUpdate方法,这将导致循环调用,并导致浏览器在内存已满时崩溃。
setState调用栈
既然setState最终通过enqueueUpdate进行状态更新,那么enqueue update到底是如何更新状态的呢?
首先看下面这个问题。你能正确回答吗?
从“react”导入React,{ Component }
类示例扩展组件{
构造函数(){
超级()
this.state={
瓦尔:0
}
}
componentidmount(){
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() {
返回null
}
}在上面的代码中,console.log打印四次的val分别是:0,0,2,3。
如果结果和你心目中的答案不完全一样,你想知道enqueueUpdate做了什么吗?
下图是简化的setState调用栈,注意核心的状态判断。
SetState简化调用堆栈
解密setState
是如何导致setState的各种表现的?
首先,我们需要知道事务如何与setState的不同性能相关联。首先,我们简单的对四种setstates进行分类。前两次因为在同一个调用栈中执行,属于一个范畴,setTimeout中的两个setstates因为也在同一个调用栈中执行,属于另一个范畴。我们来分析一下这两种类型的setState的调用栈。
componentDidMount中直接调用两次的setState的调用栈比较复杂;但是在setTimeout中调用两次setState的调用栈就简单多了。让我们来关注一下第一种setState的调用栈。我们发现了batchedUpdates方法,该方法早在调用setState之前就已经存在于batchedUpdates执行的事务中了。
那么,谁调用了batchedUpdates方法呢?我们再往前一层,原来是ReactMount.js中的_renderNewRootComponent方法也就是说,React组件渲染成DOM的整个过程都在一个大事务中。
接下来的解释是合乎逻辑的,因为batchingStrategy的isBatchingUpdates在componentDidMount中调用setState时已经被设置为true,所以两个setState的结果并没有立即生效,而是被放入了dirtyComponents中。这也解释了this.state.val两次都为0的原因,因为新状态还没有应用到组件。
componentDidMount中setState的调用堆栈
setTimeout中setState的调用堆栈
另一方面,setTimeout中的两个setState,由于没有前面的batchedUpdate调用,batchingStrategy的isBatchingUpates标志位为false,导致新状态立即生效,不需要转到dirtyComponents分支。即在setTimeout中第一次执行setState时,this.state.val为1,setState打印结束时this.state.val变为2。第二个setState是相同的。
在前面介绍事务时,React源代码中的许多应用程序也提到了它。如initialize、perform、close、closeAll、motifyAll等方法。出现在调用堆栈中,所有这些都表明它们当前在一个事务中。
既然事务如此有用,那么我们在编写应用程序代码时是否可以使用它们呢?可惜答案是否定的,虽然React并不建议我们直接使用事务,但是在React 15.0之前的版本中为开发者提供了batchedUpdates方法,可以解决初始示例中setTimeout中两个setState导致两个渲染的情况:
从“teact-dom”导入ReactDOM,{ unstable _ batchedUpates }
unstable _ batched pates(()={
this . setstate(val:this . state . val 1)
this . setstate(val:this . state . val 1)
})在React 15.0及以后的版本中,API batchUpdates已经被完全移除,不再建议开发者使用。
总结
在使用React的setState的过程中,要知道setState的实现原理,对setState的异步更新、setState的循环调用的风险、setState的调用栈等有比较全面的了解。让我们在遇到相关问题的时候可以更放心。
有关编程的更多信息,请访问:编程入门!这就是React中setState的更新机制的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。