react-setState mécanisme de mise à jour-analyse du code source

mise à jour asynchrone 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。

Pile d'appels setState

既然 setState 最终是通过 enqueueUpdate 执行 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); // 第 1 次输出
 this.setState({
    
    val: this.state.val + 1}); 
 console.log(this.state.val); // 第 2 次输出
 setTimeout(() => {
    
     
 this.setState({
    
    val: this.state.val + 1}); 
console.log(this.state.val); // 第 3 次输出
 this.setState({
    
    val: this.state.val + 1}); 
console.log(this.state.val); // 第 4 次输出
 }, 0); 
 } 
 render() {
    
     
 return null; 
 } 
} 
上述代码中, 4 次 console.log 打印出来的 val 分别是:0023。
假如结果与你心中的答案不完全相同,那么你应该会感兴趣 enqueueUpdate 到底做了什么?
function enqueueUpdate(component) {
    
     
 ensureInjected(); 
 // 如果不处于批量更新模式
 if (!batchingStrategy.isBatchingUpdates) {
    
     
 batchingStrategy.batchedUpdates(enqueueUpdate, component); 
 return; 
 } 
 // 如果处于批量更新模式,则将该组件保存在 dirtyComponents 中
 dirtyComponents.push(component); 
} 
如果 isBatchingUpdates 为 false,则对所有队列中的更新执行 batchedUpdates 方法,否则只
把当前组件(即调用了 setState 的组件)放入 dirtyComponents 数组中。例子中 4 次 setState 调
用的表现之所以不同,这里逻辑判断起了关键作用。
那 么 batchingStrategy 究竟做什么呢?其实它只是一个简单的对象,定义了一个
isBatchingUpdates 的布尔值,以及 batchedUpdates 方法 

var ReactDefaultBatchingStrategy = {
    
     
 isBatchingUpdates: false, 
 batchedUpdates: function(callback, a, b, c, d, e) {
    
     
 var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates; 
 ReactDefaultBatchingStrategy.isBatchingUpdates = true; 
 if (alreadyBatchingUpdates) {
    
     
 callback(a, b, c, d, e); 
 } else {
    
     
 transaction.perform(callback, null, a, b, c, d, e); 
 } 
 }, 
} 

Si isBatchingUpdates 为 false, alors effectuez toutes les mises à jour dans la batchedUpdatesméthode de file d'attente , sinon, seul
le composant actuel (c'est-à-dire, appelez les setState 的composants) dans le dirtyComponentstableau.


** Remarque -------- ** Le texte original ici est si isBatchingUpdates 为 true, mais du point de vue du code, la méthode de mise à jour doit être exécutée lorsque le lot est mis à jour. S'il s'agit d'une mise à jour par lots, elle entrera la dirtyfile d'attente.
Donc je pense que c'est:

如果 `isBatchingUpdates 为 false`,则对所有队列中的更新执行 `batchedUpdates` 方法,否则只
把当前组件(即调用了 `setState 的`组件)放入 `dirtyComponents` 数组中。

Insérez la description de l'image ici

Insérez la description de l'image ici
4. En résumé

  • Exécution asynchrone sous code synchrone
  • Exécution synchrone sous code asynchrone

Exécution asynchrone sous code synchrone

Sous les rappels sans événement et setTimeout, tels que le cycle de vie de react, le code de setState est exécuté de manière asynchrone.

constructor() {
    
    
    super();
    this.state = {
    
    
      val: 0,
    };
  }
  componentDidMount() {
    
    
    console.log(this.state.val, '1'); // 0
    this.setState({
    
    
      val: this.state.val + 1,
    });
     this.setState({
    
    
      val: this.state.val + 1,
    });
    console.log(this.state.val, '2'); // 0
    console.log(this.state.val, '3'); // 0
  }
  componentDidUpdate() {
    
    
    console.log('did');
    console.log(this.state.val);
  }
原因是:react源码中有一个 `isBatchingUpdates: false`,在每个事务开始前会被置为 true,事务结束后的close会被再置回false;

isBatchingUpdates 为 **true**,所以并不会直接执行更新 state,而是加入了 dirtyComponents,,等待后续执行,所以此时会被异步挂起。所以打印时获取的都是更新前的状态 0。

Un autre point important est que plusieurs setStates asynchrones seront combinés et exécutés, donc componentDidUpdate ne sera exécuté qu'une seule fois, et il vaut 1.

Exécution synchrone de code asynchrone

Si votre setState est encapsulé par setTimeout, ou le callback fn dans la fonction d'événement, alors setState sera exécuté de manière synchrone . Et il ne sera pas fusionné et exécuté , ce qui entraînera le rendu du composant plusieurs fois.

  componentDidMount() {
    
    
    setTimeout(() => {
    
    
      console.log(this.state.val, '1');
      this.setState({
    
    
        val: this.state.val + 1,
      });
      this.setState({
    
    
        val: this.state.val + 1,
      });
      console.log(this.state.val, '2');
      console.log(this.state.val, '3');
    }, 0);
  }
  componentDidUpdate() {
    
    
    console.log('did');
    console.log(this.state.val);
  }

Le résultat de l'exécution est donc

0 "1"
did
1
did
2
2 "did"
2 "did"

setState est exécuté de manière synchrone et le composant est de nouveau rendu à chaque fois.

Explication de la source car une fonction de rappel a été exécutée de manière asynchrone après la clôture de la transaction, et cette fois isBatchingUpdatesdéjà false, et elle sera mise à jour directement. C'est donc une exécution synchrone.


Ce qui précède est un résumé de mon propre processus d'apprentissage, et le partage avec tout le monde pour apprendre ensemble.
Partiellement résumé de "Deep into the React Technology Stack"
Insérez la description de l'image ici

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45416217/article/details/112424751
conseillé
Classement