React (prior to v.18) batches state updates to be only one update, so that React will rerender only once, but there are some limitations to it, it said to be able to batches only state updates are in React event handlers.

Example 1 (React prior to v18)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Example 1, React v17
// Index.js
import App from './App';

const container = document.getElementById('root');
ReactDOM.render(<App/>, container);


// -----------------------------------------------
// App.js
import React, { useState } from 'react';

export default function App() {
  const [data1, setData1] = useState(false);
  const [data2, setData2] = useState(false);
 
  const handleClick = () => {
    setData1(!data1);
    setData2(!data2);
  }

  console.log('component is being re-rendered');
  
  return (
    <div>
      <button onClick={handleClick}>Test</button>
    </div>
  );
}

If we click a button, we will see only one

component is being re-rendered

But if we do something like this Example 2 (React prior to v18)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Example 2, React v17
// Index.js
import App from './App';

const container = document.getElementById('root');
ReactDOM.render(<App/>, container);


// -----------------------------------------------
// App.js
import React, { useState } from 'react';

export default function App() {
  const [data1, setData1] = useState(false);
  const [data2, setData2] = useState(false);
 
  const handleClick = () => {
    // wrap state updates inside setTimeout
    setTimeout(() => {
      setData1(!data1);
      setData2(!data2);
    });
  }

  console.log('component is being re-rendered');
  
  return (
    <div>
      <button onClick={handleClick}>Test</button>
    </div>
  );
}

If we click a button, we will see

component is being re-rendered
component is being re-rendered

Again, React prior to v18, batches state updates only in React event handlers e.g. onClick, onChange … But if we wrap state updates inside setTimeout, Promise, Native event handlers. React will not batch those events, as we can see in Example 2 (above)

But now React v18, solves those issues, but we have to change the way to render our App a bit Example 3 (React v18)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Example 3, React v18
// Index.js
import ReactDOM from 'react-dom';
import App from './App';

const root = ReactDOM.createRoot(container);
root.render(<App/>);


// -----------------------------------------------
// App.js
import React, { useState } from 'react';

export default function App() {
  const [data1, setData1] = useState(false);
  const [data2, setData2] = useState(false);
 
  const handleClick = () => {
    setTimeout(() => {
      setData1(!data1);
      setData2(!data2);
    });
  }

  console.log('component is being re-rendered');
  
  return (
    <div>
      <button onClick={handleClick}>Test</button>
    </div>
  );
}

If we click a button we will see only one

component is being re-rendered

Woww Amazing!!

btw, if we cannot migrate to v18 just yet, there is a function unstable_batchedUpdates from react-dom, but as we can imagine by its name, it is not stable.