2

Here is my code :

const {useState, useEffect} = React;

function App() {
   let [count, setCount] = useState(0);
   let [num, setNum] = useState(0);

   useEffect(() => {
       setCount((pre) => {
           return pre + 1;
       });
       setNum(num + 1);
   }, []);

   return (
     <p>
         count:{count} num:{num}
     </p>
  );
}


ReactDOM.createRoot(document.querySelector("#app"))
  .render(<React.StrictMode><App /></React.StrictMode>);
<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 id="app"></div>

The result is : count:2 num:1! why?

In theory, num and count should be synchronous, I don't know what happend!
In dev mode, useEffect's callback executed twice.

Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
chi chen
  • 21
  • 3
  • "In theory, num and count should be synchronous" -- setters are async according to React. Anyway, this works fine for me. I see 1, 1. – ggorlen Mar 01 '23 at 02:10
  • Checkout the values of `num` and `pre` and you'll see why. The `num` inside `useEffect` is in a closure that doesn't update because the dependencies doesn't include `num`. The `pre` on the other hand is called with the updated value, because this is how setting the state with a callback works. – Ori Drori Mar 01 '23 at 02:12
  • 1
    Please have a look at this question that might help you understand what happens in your code: https://stackoverflow.com/questions/61254372/my-react-component-is-rendering-twice-because-of-strict-mode – indyteo Mar 01 '23 at 02:16
  • Yeah, definitely a strict mode thing. If you add `` around `` in the above example, there's the result. – ggorlen Mar 01 '23 at 02:19
  • Does this answer your question? [My React Component is rendering twice because of Strict Mode](https://stackoverflow.com/questions/61254372/my-react-component-is-rendering-twice-because-of-strict-mode) – ggorlen Mar 01 '23 at 02:19
  • thanks guys! i'll learn about theclosure about this! – chi chen Mar 01 '23 at 02:23
  • i know why this rendering twice! my doubt is why num and count are not synchronization! setNum and setCount all executed twice! in my opinion, they are all 2 – chi chen Mar 01 '23 at 02:27
  • 1
    @chichen The reason is that the callback in the `useEffect` is defined once, but executed twice. When *defined*, the value of `num` is `0`, so `num + 1` will always be equals to `1`, whatever the number of times it is *executed*. However, the `count` is updated using its previous value, in the updater function. This is why, when called twice, it gets incremented twice as well. – indyteo Mar 01 '23 at 02:40
  • @Ori Drori is right! The issue is caused by the closure. – chi chen Mar 01 '23 at 02:42

2 Answers2

2

This is because of StricMode :

Strict Mode only runs in development, which does not impede the production build. It logs extra warnings or errors and invokes functions twice to ensure that the expected results always occur

you may wonder why they have different values and you think that they should have the same, either 1 or 2

This is because when you update a state value with the function like you did for count :

setCount((pre) => {
   return pre + 1;
});

This will make the state updates based on it previous values not based on its value in the last render
and this is why we use the function of useState so to have access to the previous value of the state before the next render and we can also update it again just before the component rerenders as it happend in this example

So here the second time useEffect runs (because of strictMode) pre is equal to 1 so the final value will be 2.
However when you update your state this way :

setNum(num + 1);

It will be updated based on num and num is still equal to 0 because the component didn't rerendered yet

Ahmed Sbai
  • 10,695
  • 9
  • 19
  • 38
0

The prime reason behind this is StrictMode

StrictMode renders components twice (on dev but not production) in order to detect any problems with your code and warn you about them (which can be quite useful).

If you want to get rid of this, change

 ReactDOM.render(
     <React.StrictMode>
       {app}
     </React.StrictMode>,
    document.getElementById('root')
  );

to

  ReactDOM.render(
    {app}
    document.getElementById('root')
  );

krishnaacharyaa
  • 14,953
  • 4
  • 49
  • 88