为什么setTimeout中的setState没有批处理



代码:

useEffect(() => {
setTimeout(() => {
// this cause re-render twice
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
}, 1000);
}, []);

我的问题是,如果我们按顺序调用两个setCount,为什么这个组件会重新渲染两次
React不是一次处理多个setCount
感谢您的回答或任何建议。

代码沙箱示例

React不是一次性批处理多个setCount吗?

如果可以的话。这是一个不可能的情况。

为了对批处理事物做出反应,代码的执行需要从反应本身开始。例如,任何组件生命周期事件,或任何合成事件回调(例如,<button>onClick(。当这种情况发生时,react可以让你的代码继续运行,根据你的喜好排队,然后一旦你完成了,你就会返回到react的代码,然后它可以呈现排队的更改。

但是,如果代码执行不是从react开始的,那么一旦返回,就不会返回到react的代码。因此,由于他们无法在您的代码之后运行代码,因此他们会立即进行渲染。

React将批处理状态更新,如果它们是从基于React的事件中触发的,比如从事件处理程序中触发的或者是同步调用的。如果它们是在React之外触发的,比如setTimeout()Promise回调,它将不会批量更新。

在您的示例中,setCount是从计时器的上下文中调用的,因此没有发生批更新。但是,如果您从事件处理程序的上下文中多次调用setCount或同步调用,则状态更新将按如下代码段所示进行批处理。

请注意,计时器和promise回调不会批量更新:

function App() {
const [count, setCount] = React.useState(0);
console.log("re-render");
React.useEffect(() => {
setTimeout(() => {
// This cause re-render twice
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
}, 2000);
// Will be batched as it it called 
// In the context of React
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
//The state updates from the promise callback
//Will not be batched
Promise.resolve(1).then(data => {
console.log("Promise Resolved");
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
}); 

}, []);
const clickHandler = () => {
// Will be batched
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
};
return (
<div className="App">
<h1>{count}</h1>
<button onClick={clickHandler}>Click</button>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="root"></div>

其他答案解释了它为什么要多次渲染。

现在,我想给出一个解决方案,使其只渲染一次。有一个API只允许您进行一次渲染:ReactDOM.unstable_batchedUpdates

在您的示例中:

ReactDOM.unstable_batchedUpdates(() => {
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
});
}, 1000);

https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html#automatic-分批

现在,这是React自己处理的事情。

最新更新