Boison
Boison

Reputation: 35

Why React re-render third times when dependencies of useEffect is object?

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

Answers (1)

CertainPerformance
CertainPerformance

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

Related Questions