Why is the component rerendering after setting state?

68 Views Asked by At
export function App(props) {
  const [number,setNumber] = useState(0);
  console.log(number,'one')
  useEffect(()=>{
    console.log(number,'two')
    setNumber(1);
  },[number]);
  console.log(number,'three')
  return (
    <div className='App'>
      {number}
    </div>
  );
}

const { useState, useEffect } = React;

function App(props) {
  const [number,setNumber] = useState(0);
  console.log(number,'one')
  useEffect(()=>{
    console.log(number,'two')
    setNumber(1);
  },[number]);
  console.log(number,'three')
  return (
    <div className='App'>
      {number}
    </div>
  );
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

Given the code as above, I expected the output of console.log to be as follows

Expected output
0 one
0 three
0 two
1 one
1 three
1 two

Because during the initial rendering, the value of 'number' changed to 1. In the subsequent renderings, even if setNumber(1) is called, I expected 'number' not to change, so I thought there would be no re-rendering.

However, the actual output was as follows

Actual output
0 one
0 three
0 two
1 one
1 three
1 two
1 one
1 three

I was under the impression that if the state values are the same, no re-render would occur. However, even though the state values did not change, why is there an additional re-render happening?

1

There are 1 best solutions below

0
On BEST ANSWER

This sequence can be explained by the following order of events.

  1. Your App component renders with the initial value 0 for number. This causes the output 0 one and 0 tree.
  2. After the render is complete the useEffect callback is fired. This causes the output 0 two.
  3. Because useEffect called setNumber (from 0 to 1) React will register a state updated and re-render the component. Causing the output 1 one and 1 tree.
  4. Because the useEffect number dependency changed from 0 to 1 the useEffect callback is invoked again. Causing the output 0 two.
  5. Because useEffect called setNumber (from 1 to 1) React will register a state update (even though you use the same value) and re-render the component. Causing the output 1 one and 1 tree.
  6. The useEffect callback is not called this time because the dependency number did not change. So this is where the render stops.

If you want to avoid the last render, you should not call setNumber if the value is already what it should be.

// avoid `setNumber(1)` if `number` is already `1` to prevent the final render
if (number != 1) setNumber(1);

const { useState, useEffect } = React;

function App(props) {
  const [number,setNumber] = useState(0);
  console.log(number,'one')
  useEffect(()=>{
    console.log(number,'two')
    if (number != 1) setNumber(1);
  },[number]);
  console.log(number,'three')
  return (
    <div className='App'>
      {number}
    </div>
  );
}

ReactDOM.createRoot(document.querySelector("#root")).render(<App />);
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>