首頁 > web前端 > js教程 > 主體

深入了解React中setState的更新機制

青灯夜游
發布: 2022-01-07 19:22:59
轉載
3575 人瀏覽過

setState作為react中的重要部分,將對元件 state 的變更排入佇列,並通知 React 需要使用更新後的 state 重新渲染此元件及其子元件。以下這篇文章帶大家了解React中的setState機制,希望對大家有幫助!

深入了解React中setState的更新機制

stateReact中的重要概念。我們知道,React是透過狀態管理來實現對元件的管理。那麼,React是如何控制元件的狀態的,又是如何利用狀態來管理元件的呢? 【相關推薦:Redis影片教學

我們都知道,React透過this.state來存取state ,透過this.setState()方法更新state。當this.setState()被呼叫的時候,React會重新呼叫render方法來重新渲染UI

setState已經是我們非常熟悉的一個API,然而你真的了解它嗎?下面我們將一起來解密setState的更新機制。

setState非同步更新

大家剛開始寫React的時候,通常會寫出this.state.value = 1 這樣的程式碼,這是完全錯誤的寫法。

注意:絕對不要直接修改 this.state,這不僅是一種低效的做法,而且很有可能會被之後的操作替換。

setState透過一個佇列機制實作state更新。當執行setState時,會將需要更新的state合併後放入狀態對列,而不會立刻更新this.state,佇列機制可以高效地批量更新state。如果不透過setState而直接修改this.state的值,那麼該state將不會被放入狀態佇列中,當下次呼叫 setState 並對狀態佇列進行合併時,將會忽略先前直接被修改的 state,而造成無法預測的錯誤。

因此,應該使用setState 方法來更新state,同時React 也正是利用狀態佇列機制實現了setState的非同步更新,避免頻繁地重複更新state。相關程式碼如下:

// 将新的state合并到状态更新队列中
var nextState = this._processPendingState(nextProps, nextContext);

// 根据更新队列和 shouldComponentUpdate 的状态来判断是否需要更新组件
var shouldUpdate = this._pendingForceUpdte || !inst.shouldCompoonentUpdate || inst.shouldComponentUpdate(nextProps, nextState, nextContext0;
登入後複製

setState循環呼叫風險

當呼叫setState時,實際上會執行enqueueSetState 方法,並對 partialState 以及_pendingStateQueue 更新佇列進行合併操作,最終操作enqueueSetState 執行state 更新。

performUpdateIfNecessary 方法會取得_pendingElement、_pendingStateQueue、_pendingForceUpdate,並呼叫receiveComponentupdateComponent# 方法進行元件更新。

如果在shouldComponetUpdatecomponentWillUpdate 方法中呼叫setState, 此時this._pendingStateQueue != null, 則performUpateIfNecessary 方法就會呼叫updateComponent 方法進行元件更新,但updateComponent 方法又會呼叫shouldComponentUpdate方法,因此造成循環調用,使得瀏覽器記憶體佔滿後崩潰。

深入了解React中setState的更新機制

setState呼叫堆疊

既然

setState 最終是透過enqueueUpate#state#更新,那麼enqueueUpdate 到底是如何更新state 的呢?

首先,看看下面這個問題,你是否能夠正確回答呢?

import React, { Component } from 'react'

class Example extends Component {
  constructor() {
    super()
    this.state = {
      val: 0
    }
  }
  
  componentDidMount() {
    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() {
    return null
  }
}
登入後複製

上述程式碼中, 4 次

console.log 列印出來的 val 分別是:0、0、2、3

假如結果與你心中的答案不完全相同,那麼你是否想知道

enqueueUpdate 到底做了什麼? 下圖是一個簡化的 setState 呼叫棧,注意其中核心的狀態判斷。

深入了解React中setState的更新機制

setState簡化呼叫堆疊

解密setState

#到底是怎麼導致

setState的各種不同表現的呢?

我们先要了解事务跟 setState 的不同表现有什么关系。首先,我们把4次 setState 简单归类,前两次属于一类,因为他们在同一次调用栈中执行,setTimeout 中的两次 setState 属于另一类,因为他们也是在同一次调用栈中执行。我们分析一下这两类 setState 的调用栈。

componentDidMount 中直接调用的两次 setState,其调用栈更加复杂;而setTimeout 中调用的两次 setState,其调用栈则简单很多。下面我们重点看看第一类 setState 的调用栈,我们发现了 batchedUpdates 方法,原来早在 setState 调用前,已经处于batchedUpdates执行的事务中了。

batchedUpdates方法,又是谁调用的呢?我们再往前追溯一层,原来是 ReactMount.js 中的 _renderNewRootComponent方法。也就是说,整个将React组件渲染到DOM中的过程就处于一个大的事务中。

接下来的解释就顺理成章了,因为在componentDidMount中调用setState时,batchingStrategyisBatchingUpdates 已经被设为true,所以两次setState的结果并没有立即生效,而是被放到了dirtyComponents中。这也解释了两次打印 this.state.val 都是 0 的原因,因为新的 state 还没有被应用到组件中。

深入了解React中setState的更新機制

componentDidMountsetState的调用栈

深入了解React中setState的更新機制

setTimeoutsetState的调用栈

再反观 setTimeout 中的两次setState,因为没有前置的 batchedUpdate 调用,所以 batchingStrategyisBatchingUpates 标志位是false,也就导致了新的 state 马上生效,没有走到 dirtyComponents 分支。也就是说,setTimeout 中第一次执行 setState 时,this.state.val1, 而 setState 完成打印后打印时 this.state.val 变成了2。第二次的 setState 同理。

前面介绍事务时,也提到了其在 React 源码中的多处应用,像 initialize、perform、close、closeAll、motifyAll 等方法出现在调用栈中,都说明当前处于一个事务中。

既然事务这么有用,我们写应用代码时能使用它吗?很可惜,答案是不能。尽管React不建议我们直接使用事务,但在 React 15.0 之前的版本中还是为开发者提供了 batchedUpdates 方法,它可以解决针对一开始例子中setTimeout 里的两次 setState 导致两次 render 的情况:

import ReactDOM, { unstable_batchedUpates } from 'teact-dom'

unstable_batchedUpates(() => {
  this.setState(val: this.state.val + 1)
  this.setState(val: this.state.val + 1)
})
登入後複製

React 15.0 以及之后版本中,已经彻底将 batchUpdates 这个 API 移除了,因此不再建议开发者使用它。

总结

在使用ReactsetState的过程中,了解setState的实现原理,对setState异步更新、setState循环调用风险、setState调用栈等进行更加全面的了解,才能让我们在遇到相关问题的时候更加游刃有余。

更多编程相关知识,请访问:编程入门!!

以上是深入了解React中setState的更新機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:juejin.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!