为什么在setTimeout中使用setState不会批处理?

4

代码:

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

我的问题是:为什么如果我们连续调用两个setCount,这个组件会重新渲染两次?难道React不能一次性批量处理多个setCount吗?感谢您的回答或任何建议。

codesandbox示例

4个回答

7
React是否会批量处理多个setCount?
如果可以,它就会这样做。但是在本例中不行。
为了使React可以批处理,代码的执行需要始于React自身。例如,任何组件生命周期事件或任何合成事件回调(例如<button>onClick)。当发生这种情况时,React可以让您的代码继续运行,排队等待尽可能多的设置状态,然后一旦完成,您将返回到React的代码,它可以渲染已排队的更改。
但是,如果代码执行未始于React,则一旦返回,您将不会返回到React的代码。因此,由于他们无法在您的代码之后运行代码,他们会立即执行渲染。

谢谢你的回答,你的解释非常清晰! - roy fang

1
如果在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>


1

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

现在,我想提供一个解决方案,使其只渲染一次。有一个API可以让您只进行一次渲染:ReactDOM.unstable_batchedUpdates

在您的示例中:

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接