Reputation: 8765
I have a simple React component and I set the same value each time that I click on the button:
import React, { useState } from 'react';
import './style.css';
let data = { title: 'ABC' };
export default function App() {
const [foo, setFoo] = useState();
console.log('Rendered');
return (
<div>
<button onClick={() => setFoo(data)}>Set Data</button>
<h1>Data: {JSON.stringify(foo)}</h1>
</div>
);
}
But renders are a little bit strange because at the second button click React renders the component but I set the same value. Why React re-render the component although I set the same value?
Upvotes: 3
Views: 1866
Reputation: 53874
The question is about why the component renders although the new state equals the previous state (shallow comparison)
// On second button click
const prevState = data
// State trigger
setFoo(data)
// Same state, it doesn't triggers a render
data === prevState
So, the component didn't trigger render due to state change.
But it happened due to another reason, as mentioned in docs "between the lines" under Hooks API Bailing out of a state update section:
Although in some cases React may still need to call your component before skipping the children, it shouldn’t affect your code (react.dev).
Note that React may still need to render that specific component again before bailing out (legacy.reactjs.org).
Unlike in class component, for function components, after setting the same state like in our case, sometimes, React will need another render to validate its equality. Its unfortunate edge case.
But it should not consider you as "performance issue" since it does not effect the React.Node
tree, it won't continue in the reconciliation process if the state didn't change. It even won't make unnecessary hooks calls.
Same goes here, there is another render for bail out, another log of "A".
Although there is a "bail out" render, notice that the useEffect
does not run.
import React, { useEffect } from "react";
import ReactDOM from "react-dom";
const App = () => {
const [state, setState] = React.useState(0);
useEffect(() => {
console.log("B");
});
console.log("A");
return (
<>
<h1>{state}</h1>
<button onClick={() => setState(42)}>Click</button>
</>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
If you wondering on logs order ("Why 'A' logged before 'B'?"), try deep diving another question: React useEffect
in depth / use of useEffect
?
Upvotes: 5
Reputation: 123
In functional component, a component isn't re-rendered if it's the same value, i.e. a value that passes === comparison:
const [state, setState] = useState({});
...
setState(state => state); // no re-render
Otherwise a component is re-rendered:
setState(state => ({...state})); // re-render
As commented already, it may be "rerendered" by react, but the DOM will likely not change.
Checkout https://github.com/facebook/react/issues/17474
Upvotes: 1