最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why setState in setTimeout not batching? - Stack Overflow

programmeradmin2浏览0评论

Code:

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

My question is why this ponent re-render twice if we call two setCount in sequence.
Doesn't React batch multiple setCount in one go?
Thank you for answering or any suggestion.

codesandbox example

Code:

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

My question is why this ponent re-render twice if we call two setCount in sequence.
Doesn't React batch multiple setCount in one go?
Thank you for answering or any suggestion.

codesandbox example

Share Improve this question edited Dec 23, 2020 at 15:35 Mario Petrovic 8,35215 gold badges43 silver badges66 bronze badges asked Dec 23, 2020 at 14:46 roy fangroy fang 413 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 8

EDIT (2023): React 18 has changed this behavior and now even this case will be batched.

Original answer (2020):


Doesn't React batch multiple setCount in one go?

It does if it can. This is a case where it can not.

In order for react to batch things, the execution of code needs to start within react itself. For example, any ponent lifecycle events, or any synthetic event callbacks (eg, the onClick for a <button>). When that happens, react can let your code keep running, queuing up as many set states as you like, and then once your done you will return to react's code, which can then render the queued changes.

But if code execution did not start from react, then once you return, you're not going to be returning to react's code. So since they won't get to run code after yours, they do the render right away.

The other answers explained why it is rendering several times.

Now, I want to give a solution to make it render only once. There is an API that allows you to make only one render: ReactDOM.unstable_batchedUpdates

In your example:

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

React will batch state updates if they're triggered from within a React-based event, like from inside a event handler or if they are called synchronously. It will not batch updates if they're triggered outside of React, like a setTimeout() or a Promise callback.

In your example the setCount is called from the context of a timer hence the batch updates did not happen. But if you call setCount multiple times from the context of a event handler or called synchronously, the state updates will be batched as shown in the snippet below.

Notice that the timer and the promise callbacks do not batch the updates:

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./react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg./react-dom@17/umd/react-dom.development.js"></script>

<div id="root"></div>

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

Now, it's something React handles itself.

发布评论

评论列表(0)

  1. 暂无评论