Reputation: 35
result of below code is (you can check it in codesandbox)
start
=useEffect=
start
=useEffect=
start
I know the second render is caused by the change of setState (2 -> 3)
and in the second lifecycle, it can enter useEffect due to the change of object v1.key1
but what happened in the third render? why does it have a third render? (p.s. I've tried strict mode. the same problem!)
`
function App() {
console.log("start");
const v1 = { key1: { key2: 2 } };
const [tryone, setTryone] = useState(2);
useEffect(() => {
console.log("=useEffect=");
setTryone(3);
}, [v1.key1]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
`
I think i expected result is like that
start
=useEffect=
start
=useEffect=
Upvotes: 2
Views: 1110
Reputation: 370779
Objects with the same contents aren't equal to each other. For another example outside of React:
let outerObj;
const Component = () => {
const obj = {};
if (outerObj) {
// Second execution
console.log(outerObj === obj);
} else {
// First execution
outerObj = obj;
}
};
Component();
Component();
Your App
is called twice, and each time it's called, the const v1 = { key1: { key2: 2 } };
creates a new object - just like the snippet above runs const obj = {}
twice, creating an object that isn't equal to the one generated in the prior call.
Your v1.key1
changes every render, so
useEffect(() => {
console.log("=useEffect=");
setTryone(3);
}, [v1.key1]);
may as well be
useEffect(() => {
console.log("=useEffect=");
setTryone(3);
});
which runs the effect every render. After the change from 2 to 3, there's a rerender, so the effect runs again.
You need to create a stable reference for v1
somehow - use useMemo
, useState
, or useRef
.
function App() {
console.log("start");
const v1 = React.useMemo(() => ({ key1: { key2: 2 } }), []);
const [tryone, setTryone] = React.useState(2);
React.useEffect(() => {
console.log("=useEffect=");
setTryone(3);
}, [v1.key1]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
</div>
);
}
ReactDOM.createRoot(document.querySelector('.react')).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 class='react'></div>
Upvotes: 2