Is React un-optimized?
Tags: react, javascript, webdev, programming
Published: 2023-07-13 13:29:21

I recently saw a question in StackOverflow which had the below code snippet:

```
export default function Mycomponent() {
  const [number,setNumber] =useState(0);
  const [string,setstring] =useState('');
  
  function func(event){
    setstring(event.target.value);
    setTimeout(()=>{
      setNumber(number+1);
    },3000);   
  }
  console.log(number,string);
  
  return (
    <>
      <input type='text'onChange={(e)=>{func(e)}} value={string}/>
    </>
  )
}
```

The user was confused with the log output:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qcisqsozcodtnw3xfzow.png)


If like me, your initial thought was that the output should have `1 'abcd'` only once, then this post might be useful for you.



### Multiple re-renders

Suppose the user starts entering `a`,`b`,`c`,`d` sequentially into the input. It goes without saying that every character typed will set a timeout. So `setTimeout` will be called 4 times. The callback in the timeout will be scheduled to run after **atleast** 3seconds. So there will be multiple `setNumber` calls, which will further lead to re-renders. 

`setstring` changes the value of the input. After 3s, `setNumber` is executed with argument 1. So `1 abcd` is printed for the first time. There will be further calls to setNumber due to the multiple timeouts set, but all will call `setNumber(1)`. What is confusing is why the log shows `1 'abcd'` twice. Calling `setNumber(1)` multiple times should cause only one re-render.

Here is a link of the [sandbox](https://codesandbox.io/p/github/codesandbox/codesandbox-template-vite-react/main?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522horizontal%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522panelType%2522%253A%2522TABS%2522%252C%2522id%2522%253A%2522clil99wxa000b3b6mzvwxyb6f%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522panelType%2522%253A%2522TABS%2522%252C%2522id%2522%253A%2522clil99wxa000d3b6mw9ghhstl%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clil99wxa000b3b6mzvwxyb6f%2522%253A%257B%2522id%2522%253A%2522clil99wxa000b3b6mzvwxyb6f%2522%252C%2522activeTabId%2522%253A%2522clilc6a58003w3b6mhk9gk0al%2522%252C%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clil99wx9000a3b6m3ry630rz%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252FApp.tsx%2522%252C%2522id%2522%253A%2522clilc6a58003w3b6mhk9gk0al%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%252C%257B%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252Fsrc%252Fmain.tsx%2522%252C%2522id%2522%253A%2522clilc73uz00i33b6mkbwd8ulf%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%257D%252C%2522clil99wxa000d3b6mw9ghhstl%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clil99wxa000c3b6m2nl8cknq%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522TASK_PORT%2522%252C%2522taskId%2522%253A%2522yarn%2520dev%2522%252C%2522port%2522%253A5173%252C%2522path%2522%253A%2522%252F%2522%257D%255D%252C%2522id%2522%253A%2522clil99wxa000d3b6mw9ghhstl%2522%252C%2522activeTabId%2522%253A%2522clil99wxa000c3b6m2nl8cknq%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showSidebar%2522%253Atrue%252C%2522sidebarPanelSize%2522%253A15%257D)



### Is React un-optimized?

The above behaviour is not specific to `setTimeout` and is a detail of React. It can be observed even without a setTimeout.

```
function App() {
  const [number, setNumber] = useState(0);
  const [string, setstring] = useState("");
  function func(event) {
    setNumber(1);
  }
  console.log(number, string);
  return (
    <>
      <input
        type="text"
        onChange={(e) => {
          func(e);
        }}
        value={string}
      />
    </>
  );
}
```

The output for the above is:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2dtcd9pngax7xcppp8iq.png)

Even when calling `setNumber` twice with the same value, React causes two renders. But yes, not a third one. Here is a link to [sandbox](https://codesandbox.io/p/github/codesandbox/codesandbox-template-vite-react/csb-ws0dnp/draft/withered-frog?file=/src/App.tsx:5,1-23,2)

Does this mean React is unoptimized and causes re-render for the same value always? 



### React bailing behaviour

No, React does bail out of multiple re-renders in most cases. This works when we write `setNumber` twice in the same flow like the below. 

```
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import "./App.css";

function App() {
  const [number,setNumber] =useState(0);
  const [string,setstring] =useState('');
  function func(event){
    setstring(event.target.value);
    setNumber(number+1);
    setNumber(number+1);
  }
  console.log(number,string);
  return (
    <>
      <input type='text'onChange={(e)=>{func(e)}} value={string}/>
    </>
  )
}

export default App;
```

The output for the above is:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d88x8vfa64815ozt0wpt.png)

Here is the [link](https://codesandbox.io/p/sandbox/reverent-wilson-sc4tz3?file=%2Fsrc%2FApp.tsx%3A4%2C1) to the sandbox

As expected, calling multiple setStates is not causing multiple re-renders.

But in the original example, set state is called from different contexts (from different setTimeout callbacks) and react still re-renders the component at least once. It does bail out from re-rendring the children though and that is how React creates an optimised User interface. 

This is documented in the React [docs](https://legacy.reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update). React can bail out of renders if the previous and new state values are same, but it might have to re-render the current component. It will definitely bail our for children though and that is why, this not a performance issue. From the docs:

> Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree.

The code snippet below shows the same behaviour:

```

const TestChild = () => {
  console.log("rerender");
  return null;
};


export default function App() {
  const [number, setnumber] = useState(0);
  const [string, setstring] = useState("");
  function func(event) {
    setstring(event.target.value);
    setTimeout(() => {
      setnumber(number + 1);
    }, 3000);
  }
  console.log(number, string);
  return (
    <>
      <TestChild />
      <input
        type="text"
        onChange={(e) => {
          func(e);
        }}
        value={string}
      />
    </>
  );
}
```

The output to the above is:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pj6vqrtqcy7dw7tm432x.png)

which clearly shows that TestChild is not rendered more than it should.

Here is a link to the [sandbox](https://codesandbox.io/s/hardcore-cloud-bvrbuy?file=/src/App.js)



### Notes

It is worth noting that a re-render does not mean re-paint of the screen. Repaint is only done when there is an actual diff between the two Virtual DOMs created. Render and commits are different and this gets even more important with concurrent React.