我知道这似乎是一个不寻常的例子,但我似乎仍然无法准确解释为什么我在单击 div 后从未看到 valueB 打印在控制台上?
请注意,由于我在 setTimeout
中调用了两个 set state 调用,它们没有被批处理。
function App() {
let [a, setA] = React.useState();
let [b, setB] = React.useState();
React.useEffect(() => {
console.log('Entering useEffect', a, b);
return () => {
console.log('Entering cleanup', a, b);
setA(null);
setB(null);
};
}, [a, b]);
console.log('Render', a, b);
return (
<div
onClick={() => {
setTimeout(() => {
setA('valueA');
setB('valueB');
}, 100);
}}
>
<h1>Test App</h1>
</div>
);
}
ReactDOM.render(
<App/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
答案 0 :(得分:1)
通常 useEffect
及其清理在渲染后异步调用(在另一个堆栈上)。但是,如果您在计时器中调用 setState
并且有 useEffect
个回调,它们会被急切地调用,但是所有从清理中累积的状态变化都被调用 after setState
调用这个操作。
因此在您的示例中,当调用 setTimeout
处理程序时:
setA
时:a
状态已更改,并且将清除中的两个状态更改添加到待处理状态更改队列setB
时:首先将 valueB
应用于 b
,然后从清理中应用 null
(这里是一种批处理)< /li>
这试图模仿状态更新实际上是在点击处理程序中进行批处理时的行为(首先从点击处理程序应用状态更新,然后从 useEffect
应用)。
当您使用更新程序功能时,您可以更清楚地看到发生了什么:
function App() {
Promise.resolve().then(() => console.log("**** another stack ****"));
console.log("before useStateA");
let [a, setA] = React.useState();
console.log("between useStates");
let [b, setB] = React.useState();
console.log("after useStateB");
React.useEffect(() => {
console.log('Entering useEffect', a, b);
return () => {
console.log('Entering cleanup', a, b);
setA(() => (console.log("setting a to null from cleanup"), null));
setB(() => (console.log("setting b to null from cleanup"), null));
};
}, [a, b]);
console.log('Render', a, b);
return (
<div
onClick={() => {
setTimeout(() => {
console.log("****timer start****");
setA(() => (console.log("setting a to valueA from timer"), "valueA"));
console.log("between timer setters");
setB(() => (console.log("setting b to valueB from timer"), "valueB"));
console.log("****timer end****");
}, 100);
}}
>
<h1>Test App</h1>
</div>
);
}
ReactDOM.render(
<App/>,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>