I was studying the React hooks and inserting some console logs in the code to better understand the render flow. Then I started to simulate the setState effects sending the same value to see if React would render it again.
import { useState } from "react";
function ManComponent() {
/* States */
const [shirt, setShirt] = useState("Blue Shirt");
console.log("Rendering man with "+shirt);
/* Actions */
const changeShirt = (newShirt) => {
console.log("[Man] Changing shirt from "+shirt+" to "+newShirt);
setShirt(newShirt);
};
return (
<div>
<p>The man is using: {shirt}</p>
<div>
<button onClick={() => changeShirt("Red Shirt")}>Change to red shirt</button>
<button onClick={() => changeShirt("Blue Shirt")}>Change to blue shirt</button>
</div>
</div>
);
}
export default function App() {
console.log("Rendering app");
return (
<ManComponent />
);
}
If I click at "Change to red shirt" three times, I get two logs saying that the component is rendering. It should be just one, since I changed the value just once, right?
My problem is to understand why does the component renders two times if the state just changed once.
P.S: I tried to remove the Strict Mode, and it had no effect. I'm using React 17 version, by the way.
The React documentation addresses exactly this behavior in the useState hook API.
Bailing out of a state update
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the
Object.iscomparison algorithm.)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.
Strict mode was a good guess, since it used to trigger duplicate logs in the render cycle before React 17, though it was updated to silence these duplicate logs to avoid confusion.
Starting with React 17, React automatically modifies the console methods like
console.log()to silence the logs in the second call to lifecycle functions. However, it may cause undesired behavior in certain cases where a workaround can be used.
...which caused additional confusion, leading to another update on that strict mode logging behavior (which is not released yet I believe).
The workaround mentioned in the quote above to debug strict mode rendering with logs is to keep a module scoped copy of the log function.
const log = console.log;
const App = () => {
log('Will not be silenced by React');
return //...
};