1

I'm trying to understand how React works under the hood and there is something that I cannot found anywhere and that I can't figure out by myself.

I know that whenever we change the state of a component using setX, React compares the new representation of the Virtual DOM with the one it already has and, if there is anything to change in the real DOM, then it goes ahead and flushes those changes.

To see this in action I created a component like this one:

<div id="root"></div><script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script><script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/standalone@7.16.6/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

const {useEffect, useState} = React;

const App = () => {
  const [counter, setCounter] = useState(1);

  console.log('in component');

  useEffect(() => {
    console.log('in useEffect');
  });

  return (
    <>
      <div>{counter}</div>
      <button onClick={() => setCounter(2)}>
        Increment
      </button>
    </>
  );
};

ReactDOM.render(<App />, document.getElementById('root'));

</script>

Initially, when the component mounts, it is logged:

in component
in useEffect

The same when I click the button once. And if I click one more time, it just logs:

in component

I guess this time we don't see the in useEffect because React correctly came to the conclusion that there is nothing to change in the DOM.

Ok. It makes completely sense.

The thing is the next time I click, I'd expect to see again the in component, but nothing happens. And the same applies to the following clicks.

Why does this happen?

jsejcksn
  • 27,667
  • 4
  • 38
  • 62
  • When you click the button a second time, nothing happens, because your code sets the counter to the fixed value `2`. You can click it a million times, but the value will stay 2, and so the virtual DOM that you return won't be different from the previous render call, and so your useEffect doesn't need to run, because nothing changed. – Mike 'Pomax' Kamermans Dec 26 '21 at 20:46
  • Does this answer your question? [React: Under what conditions does setState from the useState hook cause a re-render?](https://stackoverflow.com/questions/69996447/react-under-what-conditions-does-setstate-from-the-usestate-hook-cause-a-re-ren) – jsejcksn Dec 26 '21 at 20:48
  • Not really, @jsecksn. There, you say: _When you invoke a setState function (it doesn't matter where), it initiates the reconciliation algorithm, which, in turn, invokes your component function again_. That's what I'm expecting and what is NOT happening here as I just see the `in component` log trace once after the counter is `2`, no matter how many times I click the button. – John Rodríguez Dec 27 '21 at 11:19
  • In other words: once the counter is `2`, when clicking the button, I'd expect to NOT see that `in component` anymore or to see it every time I click the button, but not once. – John Rodríguez Dec 27 '21 at 11:30
  • The question here is, **in component** should only be seen **twice**, or **as many times as the button is clicked**. The weird thing about React here is, it is seen **thrice**, not more, not less. Why? – Dunnomuch Feb 20 '23 at 01:09

1 Answers1

0

When you click the button a second time, nothing happens, because your code sets the counter to the fixed value 2:

onClick={() => setCounter(2)}

So you can click it a million times, but the value will stay 2, and so the virtual DOM that you return won't be different from the previous render call, and so your useEffect doesn't need to run, because nothing changed.

As per the documentation, useEffect runs after render, and is intended as a mechanism to run "some code side effect" as a result of your component updated. If nothing changed, then there should be no side effects.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • Yes. But, once the counter is already `2`, why do I see the `in component` log entry just once when clicking the button? – John Rodríguez Dec 27 '21 at 11:15
  • Because you are: any setState will cause a re-render, so your App function code (or if you use a real Component, the `render()` function) will always run. However, useEffect functions will only run if the result of the render pass is _different_ from what it was before. On first click, that is the case, and so your useEffect function runs, and you see the two console logs. On second, third, etc. click, that is _not_ the case, and your useEffect function won't run. – Mike 'Pomax' Kamermans Dec 27 '21 at 17:12