搜尋

首頁  >  問答  >  主體

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

在React18版本中,為什麼點選handleClick方法會出現兩次渲染,而點選handleClick2方法只會出現一次渲染?

我希望這兩種方法的輸出是相同的。誰能告訴我為什麼它們不同?

P粉506963842P粉506963842533 天前548

全部回覆(1)我來回復

  • P粉642920522

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

    我將解釋這些呼叫順序有何不同,以及觀察到的行為如何可能。

    我無法確切地告訴你 React 內部是如何批次更新狀態的, 我只是假設 React 進行了複雜的優化,這與使用 React 的開發人員無關,並且需要深入了解 React 內部,甚至可能從一個版本更改為另一個版本。 (請隨時糾正我。)

    #區別

    Promise.resolve() 安排一個新的微任務,實際上相當於 window.queueMicrotask()

    setState 函數(可能)也會安排新的微任務, 因此它們的回呼(PromisesetState)都是在同一執行階段呼叫的。

    這兩個變體的差別在於

    • handleClickA 中,在兩個 updater 函數之間呼叫 setState2 掛鉤,而
    • handleClickB 中,兩個 updater 函數都會直接依序呼叫。

    範例程式碼

    我稍微重寫了您的程式碼,以更好地說明呼叫順序:

    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. --> []
    };

    呼叫順序說明

    這裡我說明了呼叫順序。

    (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. --> []

    個人解讀

    我假設 React 嘗試對當前排隊的所有 updater 函數進行批次處理。

    即只要僅呼叫更新器函數,請嘗試將它們批次在一起僅更新一次最終狀態。

    但是,如果呼叫了新的setState 函數,React 可能會完成目前更新循環,並在呼叫下一個updater 之前啟動新的渲染週期 函數。

    我只能猜測為什麼要這樣做

    • 因為新的 setState 可能會以某種方式破壞批次處理,或
    • 如果遞歸調用新的 setState 調用,下一次渲染將會延遲太多,或者
    • React 人員仍在研究最佳優化策略及其權衡。
    • (...或這是一個錯誤。)

    回覆
    0
  • 取消回覆