react usestate更新对象,react usestate 数据更新不及时

  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的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: