skip to Main Content

can someone explain me the code so that i can clearly understand whats happening in the underhood

i tried to display the element as AM if num is odd ele display the number PM also prints the click count

the output i get is 2 2 3(clicked three times) where i expect 2 2 2

function App() {
  let [state, setThings] = React.useState('pm');
  let thing = '';
  let count = 1;
function updateThings() {
    count = count + 1;
    if (count % 2 === 0) {
      setThings((pre) => state = 'AM');
    } else {
      setThings((pre) => state = 'PM');
    }
      console.log(count);
  }

  thing = <div>{state}</div>// <Things items={{ data: state }} />;
  
  return (
    <div id="main">
      <button
        type="button"
        onClick={updateThings}
        className="w-48 h-20 bg-green-400 rounded-2xl m-20 ring-2 ring-white"
      >
        click me
      </button>
      {thing}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<div id="root"/>

3

Answers


  1. This behaviour due to second click. For the second click state will update to prev-state itself ("AM") so component will not re-render and value of count will be 2. And Third Click will update this to 3.

    Login or Signup to reply.
  2. It’s worth remembering that each time a render happens, the component will re-run from top to bottom in order to re-render the JSX. It’s really not a good idea to mix stated and unstated values because it will lead to unclear behaviour.

    Each time the component renders it will reset these values:

      let thing = '';
      let count = 1;
    

    And this is what is causing your unclear behaviour. A re-render happens whenever a stated value in the component tree is modified to a non-strict equal value (i.e. === equality). In this case we’re talking about when the state stated value changes to a new strictly different value.

    Here is a version of the code that might behave more as you expect:

    function App() {
      const [count, setCount] = React.useState(1);
      
      return (
        <div id="main">
          <button
            type="button"
            onClick={() => setCount(c => c + 1)}
            className="w-48 h-20 bg-green-400 rounded-2xl m-20 ring-2 ring-white"
          >
            click me
          </button>
          <Things items={{ data: count % 2 === 0 ? 'AM' : 'PM' }} />
        </div>
      );
    }
    

    We can derive values from data instead and make count the only thing that actually needs to be stated. Also notice that setCount returns the new value based on the old value; this how it’s meant to be written.

    This new code will mount with count as 1 so will pass 'PM' to <Things/>. When you click the button, it will update count to 2 which will cause a re-render and so <Things/> will then be passed 'AM' and so on for each further click.

    Login or Signup to reply.
  3. The root of the problem is that you are using stateless variables across renders, and possibly further confused by your mutation of state.

    As to "why" you are seeing 2,2,3:

    count will always be 1 every render. This is because let count = 1; will be ran each render and as it redeclares the variable it will "reset" to 1.

    The only way that you will get a different value than 2 in your event handler (count = count + 1;) is if there is no re-render to reset count to 1. This is what’s happening.

    Re-renders are triggered by state updates (or prop updates). Your state is changing each click like so:

    1. ‘pm’
    2. ‘AM’
    3. ‘AM’
    4. ‘PM’
    5. ‘AM’
    6. ‘AM’
    7. ‘PM’

    Notice that the first and second clicks have different values, therefore you get a re-render and count is reset to 1. But since the state value does not change between the 2nd and 3rd update, there is no re-render and count is incremented a second time making its value 3. This does cause a state update, so count is reset to 1 and the loop starts over.

    Takeaway

    1. Local variables should not be used this way. It’s unclear what your intent is, but most likely count should be it’s own state.
    2. Do not mutate state. While I don’t think this is at play here, this setThings((pre) => state = 'AM'); is incorrect. Never re-assign state (state = 'am'), and always use const for state (const [state, setState]...).
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search