From the React Docs, what I have learnt is that the component will re-render only if there is a change in the value of a state.
For instance
import React, { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
console.log("I am rendering");
const handleButtonClick = () => {
setCount(0);
};
return (
<>
<button onClick={handleButtonClick}>Increment</button>
Count value is: {count}
</>
);
}
The message I am rendering
is printed only once even if we click the button because the setCount
function is setting the value to 0
which is the present value of count
Since there is no change in the present and future value therefore, the Component does not re-render.
However, the similar behaviour is not observed when we add an extra line setCount(1)
before setCount(0)
import React, { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
console.log("I am rendering");
const handleButtonClick = () => {
setCount(1); //this line has been added extra
setCount(0);
};
return (
<>
<button onClick={handleButtonClick}>Increment</button>
Count value is: {count}
</>
);
}
In principle, there is no change in the output of the final count
value. However, if we click the button, the component re-renders and prints the message I am rendering
I could not find an explanation for this behaviour. Is this behaviour on expected lines?.
Shouldn't the component re-render only when the final value of the state is different from the current value ?
Sometimes, Reacts needs another render phase to decide if it needs a bailout. By the way, when we saying "bailout" meaning bailing out the Reconciliation process.
Notice the documentation on Bailing out a state update:
Note that React may still need to render that specific component again before bailing out.
Here is another example of such case demonstrating the idea:
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [state, setState] = React.useState(0);
useEffect(() => {
console.log("B");
}, [state]);
console.log("A");
return (
<>
<h1>{state}</h1>
<button onClick={() => setState(42)}>Click</button>
</>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
You notice the next logs and their explanations:
A // First render
B // Mount
A // State change from 0 -> 42, triggers render
B // useEffect dep array change, triggers callback
A // **Our issue**, React needs another render
The value does change when you press the button. First, it changes to 1 then to 0 but this runs very fast that you don't get to see it.
to see this, you could add a setTimeout
const handleButtonClick = () => {
setCount(1); //this line has been added extra
setTimeout(() => {
setCount(0);
}, 500);
};