Maison  >  Questions et réponses  >  le corps du texte

Problème de traitement par lots de microtâches React18

useEffect(() => {
  console.log("render");
});

const handleClick = () => {
  setC1((c) => c + 1);

  Promise.resolve().then(() => {
    setC1((c) => c + 1);
  });
};

const handleClick2 = () => {
  Promise.resolve().then(() => {
    setC1((c) => c + 1);
  });

  setC1((c) => c + 1);
};

Dans la version React18, pourquoi un seul rendu se produit en cliquant sur la méthode handleClick方法会出现两次渲染,而点击handleClick2 ?

Je veux que le résultat des deux méthodes soit le même. Quelqu'un peut-il me dire pourquoi ils sont différents ?

P粉506963842P粉506963842430 Il y a quelques jours489

répondre à tous(1)je répondrai

  • P粉642920522

    P粉6429205222023-09-08 17:06:21

    Je vais vous expliquer en quoi ces séquences d'appels diffèrent et comment le comportement observé est possible.

    Je ne peux pas vous dire exactement comment les mises à jour de React se présentent par lots en interne, Je suppose simplement que React propose des optimisations complexes qui ne sont pas pertinentes pour le développeur utilisant React et nécessitent une compréhension approfondie des composants internes de React et peut-être même un changement d'une version à l'autre. (N'hésitez pas à me corriger.)

    Différence

    Promise.resolve() 安排一个新的微任务,实际上相当于 window.queueMicrotask().

    La fonction

    setState (éventuellement) planifiera également une nouvelle microtâche, Par conséquent leurs rappels (PromisesetStatePromise et

    ) sont appelés dans la même phase d'exécution.

    La différence entre ces deux variantes est
    • handleClickA 中,在两个 updater 函数之间调用 setState2 s'accroche
    • pendant que
    • handleClickB 中,两个 updaterLes fonctions de
    • seront toutes appelées directement dans l'ordre.

    Exemple de code

    J'ai légèrement réécrit votre code pour mieux illustrer la séquence d'appel :

    const setState1 = setState;     
    const setState2 = setState;
    const update1 = updaterFunction; // c => c + 1
    const update2 = updaterFunction; // c => c + 1
    
    const handleClickA = () => {          
                                      // Scheduled functions:
        setState1( update1 );         // 1. --> [ update1 ]
        
        queueMicrotask(() => {        // 2. --> [ update1, setState2 ]
            setState2( update2 );     // 4. --> [ update2 ]
        });
    
        // update1();                 // 3. --> [ setState2 ]
        // setState2( update2 );      // 4. --> [ update2 ]
        // update2();                 // 5. --> []
    };
    
    const handleClickB = () => {
                                      // Scheduled functions:    
        queueMicrotask(() => {        // 1. --> [ setState2 ]
            setState2( update2 );     // 3. --> [ update2 ]
        });
    
        setState1( update1 );         // 2. --> [ setState2, update1 ]
        
        // setState2( update2 );      // 3. --> [ update1, update2 ]
        // update1();                 // 4. --> [ update2 ]
        // update2();                 // 5. --> []
    };

    Instructions de séquence d'appel

    Ici, j'explique la séquence d'appel.

    (FIFO

    >):

    handleClickA

    // 0. --> []
    - schedule update1 (setState1())  // 1. --> [ update1 ]
    - schedule setState2              // 2. --> [ update1, setState2 ]
    - invoke update1()                // 3. --> [ setState2 ]
    - schedule update2 (setState2())  // 4. --> [ update2 ]
    - invoke update2()                // 5. --> []
    handleClickB

    // 0. --> []
    schedule setState2              // 1. --> [ setState2 ]
    schedule update1 (setState1())  // 2. --> [ setState2, update1 ]
    schedule update2 (setState2())  // 3. --> [ update1, update2 ]
    invoke update1()                // 4. --> [ update2 ]
    invoke update2()                // 5. --> []

    Interprétation personnelle

    updaterJe suppose que React essaie de regrouper toutes les

    fonctions actuellement en file d'attente.

    c'est-à-dire que chaque fois que seule la fonction de mise à jour est appelée, essayez de les regrouper et de ne mettre à jour l'état final qu'une seule fois. Cependant, si une nouvelle

    fonction est appelée, React peut

    terminer la boucle de mise à jour actuellesetState et démarrer un nouveau cycle de rendu avant d'appeler la prochaine fonction updater. Je ne peux que deviner pourquoi cela est fait

    Parce que le nouveau

    pourrait casser le lot d'une manière ou d'une autre, ou
    • Si de nouveaux setState appels sont effectués de manière récursive, le prochain rendu sera trop retardé, ou
    • Les gens de React travaillent toujours sur les meilleures stratégies d'optimisation et leurs compromis. setState
    • (...ou c'est un bug.)
    • répondre
      0
  • Annulerrépondre