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
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.
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:
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 thestate
stated value changes to a new strictly different value.Here is a version of the code that might behave more as you expect:
We can derive values from data instead and make
count
the only thing that actually needs to be stated. Also notice thatsetCount
returns the new value based on the old value; this how it’s meant to be written.This new code will mount with
count
as1
so will pass'PM'
to<Things/>
. When you click the button, it will updatecount
to2
which will cause a re-render and so<Things/>
will then be passed'AM'
and so on for each further click.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 becauselet 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 resetcount
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:
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 andcount
is incremented a second time making its value 3. This does cause a state update, socount
is reset to 1 and the loop starts over.Takeaway
count
should be it’s own state.setThings((pre) => state = 'AM');
is incorrect. Never re-assign state (state = 'am'
), and always useconst
for state (const [state, setState]...
).