export default function App() {
const [v, setV] = useState(0);
const handleClick = async () => {
console.log(0);
setV((v) => v + 1);
Promise.resolve().then(() => {
console.log(1);
setV((v) => v + 1);
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
};
const handleClick2 = async () => {
console.log(0);
Promise.resolve().then(() => {
console.log(1);
setV((v) => v + 1);
console.log(2);
Promise.resolve().then(() => {
console.log(3);
});
});
setV((v) => v + 1);
};
console.log("render");
return (
<div className="App">
<h1 onClick={handleClick}>Hello CodeSandbox</h1>
<h2>value: {v}</h2>
</div>
);
}
执行两个click方法,分别打印
handleClick:
handleClick2
为何handleClick中自动批量更新没有生效呢
debug
了一下,原因在于handleClick
先调用了setV
将commit阶段
前置了导致的,下面详细拆解一下首先需要知道,
setV
其实就是dispatchSetState
, 它有两个核心动作v
queueMicroTask
派发一个异步调度任务,commit阶段
就包含在其中而批量合并的条件是,需要在上一个
setV
派发的commit阶段
执行之前,后续setV
的同步阶段需要执行完毕,将内存中的状态更新,这样就能共用同一个commit阶段
handleClick
分析一下就会发现,
handleClick
的执行过程显然不满足这个条件直接描述会比较抽象,直接看图,
handleClick
中的同步代码执行完后,结果如下这种场景下,清空微队列时,第一个
setV
的commit阶段
最先执行,无法与后续的更新合并handleClick2
handleClick2
中的同步代码执行完后,结果如下在这种情况下,第一个
setV
的commit阶段
被放置在了微队列的队尾,当清空微队列,执行到队尾时,后续setV
的同步逻辑已经执行完毕,内存中的状态值已经更新,这样一来就能在同一个commit阶段
完成更新了