React18微任务批处理问题
P粉506963842
P粉506963842 2023-09-07 22:30:24
0
1
487

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粉506963842
P粉506963842

全部回复(1)
P粉642920522

我将解释这些调用顺序有何不同,以及观察到的行为如何可能。

我无法确切地告诉你 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 人员仍在研究最佳优化策略及其权衡。
  • (...或者这是一个错误。)
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!