1

I have a simple react component. It has a state, and if the state is true is logs something to the console. The component also renders a button with a handler to toggle the state.

However, some really unexpected behavior is happening. When I click the button, the state is true logs twice. If I click the button again, it logs twice again. But on the third click onwards, it doesn't log anything anymore. The button was clicked inside the click handler logs each time.

I am having a similar issue in my chrome environment as well. There, the state is true only logs once per click, but again after two clicks it doesn't log anymore.

Does anyone know why the console.log runs twice per click, as well as why it only works up to two times? I would expect the component to re-render each time and then run the console log each time as the state is always true.

https://codesandbox.io/s/blissful-jepsen-luo8ce?file=/src/App.js:0-360

export default function App() {
  const [isShown, setShown] = React.useState(false);
  if (isShown) {
    console.log("the state is true");
  }
  return (
    <button
      onClick={() => {
        console.log("button was clicked");
        setShown(true);
      }}
    >
      Click Me!
    </button>
  );
}

I tried search online but could not find any answers, help is appreciated!

713sean
  • 313
  • 11

3 Answers3

0

This is because of how React functions. Because of Strict mode - React will render twice.

So if you update a state, it will render twice if what you're rendering is affected by that state.

If you go to Index.jsx and remove StrictMode around the rendering of App, you will see it will only console.log once.

As for why it only works for those only two times, it's because you only update the state hook once within your click. There is no need to re-render after since the state doesn't change

If you did something ridiculous like setShown(true);setShown(false);setShown(true); within your click then you will see the console log fire each time (twice if in Strict Mode), because the state "updates"

Not sure if that made any sense, let me know!

Max S.
  • 81
  • 4
  • Hi Max, thanks. I've removed strict mode, and I see the duplication goes away. I'm still not understanding the part about only working two times though. The way I see it is: 1) React initial state is false 2) I click the button setting state to true 3) true gets printed 4) I click the button again, but since the state is already true, why does it even print a second time? It seems like it should stop working after the first click, not the second. Furthermore, I thought functional components always re-render on setState calls even if the value is the same. – 713sean Dec 28 '22 at 03:38
0

So your first question is why is it rendering twice because:-

  1. Your app component is rendered within React.StrictMode which is what causes your code to run in strict mode and in strict mode the consoles are shown twice because each function is made to run twice in development mode

The second question is why is it consoling twice only because:-

  1. So first understand what useState is:- useState by default simply does two things only, sets a new state and causes a re-render of the function. It is asynchronous in nature so by default, methods run after it usually runs.
  2. You need to understand that the before component return console statement is
 if (isShown) {
    console.log("the state is true");
  }

will only happen when the conditional value changes.

After the component return console will happen after every react render which is when the setState value gets set

console.log("button was clicked");

Summary:- First your value is false so it renders the first console and after you set isShown to true so it renders the console another time. After that, if you are not setting a different value, it will not render the console statement.

enter image description here

From the image you will get the idea that isShown value is changed in the before return console but not in the after return console.When you click for second time after return console.log(ishown) also get updated to true. So for two times it renders the whole component. but after that there is no change isShown value so you will not able to see the first console.

Sammed
  • 66
  • 7
  • I still don't understand why the state is true appears twice, on initial load the state is false so it shouldn't log anything. – 713sean Dec 28 '22 at 08:10
  • It's all because of the asynchronous nature of setState. https://stackoverflow.com/questions/30782948/why-does-calling-react-setstate-method-not-mutate-the-state-immediately Here is the link for more understanding why it is showing twice You can cross-check by placing console.log(isShown) after setShown(true). – Sammed Dec 28 '22 at 09:19
  • I understand that the setter is async and doesn't mutate immediately, but I don't see how that relates to anything. Especially since the setter occurs at the end of the function in my example. – 713sean Dec 28 '22 at 09:36
  • I have edited my post with images and the best explanation I can provide. – Sammed Dec 28 '22 at 10:00
0
export default function App() {
  const [isShown, setShown] = React.useState(false);
  if (isShown) {
    console.log("the state is true");
    setShown(false);
  }
  return (
    <button
      onClick={() => {
        console.log("button was clicked");
        setShown(true);
      }}
    >
      Click Me!
    </button>
  );
}

Try the above code it will work properly, you were not setting the isShown state to false;

Zanaen
  • 21
  • 4
  • I was not trying to set the state to false, I thought that use state forces a re-render regardless and should console log something. – 713sean Dec 28 '22 at 07:24