ホームページ  >  記事  >  ウェブフロントエンド  >  React の setState の更新メカニズムを深く理解する

React の setState の更新メカニズムを深く理解する

青灯夜游
青灯夜游転載
2022-01-07 19:22:593595ブラウズ

react の重要な部分として、setState はコンポーネントの状態への変更をキューに入れ、更新された状態を使用してこのコンポーネントとそのサブコンポーネントを再レンダリングする必要があることを React に通知します。次の記事では、React の setState メカニズムについて説明します。お役に立てば幸いです。

React の setState の更新メカニズムを深く理解する

stateReact の重要な概念です。 React が状態管理を通じてコン​​ポーネントを管理していることはわかっています。では、React はコンポーネントの状態をどのように制御し、状態を使用してコンポーネントを管理するのでしょうか? [関連する推奨事項: Redis ビデオ チュートリアル ]

誰もが知っているように、 Reactthis.state を通じて state にアクセスします。 this.setState() メソッドを通じて state を更新します。 this.setState() が呼び出されると、Reactrender メソッドを再呼び出しして、UI を再レンダリングします。

setState はすでによく知られている API ですが、本当に理解していますか?以下では、setState の更新メカニズムを復号化します。

setState の非同期更新

誰もが初めて React を書き始めるとき、通常、this.state.value = 1 のようなコードを書きます。これは完全に間違っている。

注: this.state を直接変更しないでください。これは非効率的なアプローチであるだけでなく、後続の操作で置き換えられる可能性があります。

setStatestate は、キュー メカニズムを通じて更新されます。 setState が実行されると、更新する必要がある state はマージされて状態キューに入れられますが、this.state はすぐには更新されません。キュー メカニズムは、バッチで state を効率的に更新できます。 this.state の値が setState を使用せずに直接変更された場合、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 はマージ操作のキューを更新し、最後の操作 enqueueSetStatestate 更新を実行します。

performUpdateIfNecessary メソッドは、_pendingElement、_pendingStateQueue、_pendingForceUpdate を取得し、receiveComponent および updateComponent メソッドを呼び出して更新します。コンポーネント。

setState shouldComponetUpdate または componentWillUpdate メソッドで呼び出された場合、 this._pendingStateQueue != null、その後performUpateIfNecessary メソッドは updateComponent メソッドを呼び出してコンポーネントを更新しますが、updateComponent メソッドは ShouldComponentUpdate および componentWillUpdate# を呼び出します。 ## メソッドによりループ呼び出しが発生し、メモリがいっぱいになるとブラウザがクラッシュします。

React の setState の更新メカニズムを深く理解する

setState 呼び出しスタック

setState は最終的に enqueueUpate state を通じて実行されるため更新すると、enqueueUpdatestate をどのように更新しますか?

まず、次の質問を見てください。あなたは正しく答えることができますか?

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
  }
}

上記のコードでは、

console.log によって 4 回出力される 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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。