React state looks simple on the surface. You hold a value, you update it, the screen changes. In practice, I kept making the same mistakes for a while, and they all came from treating state like an ordinary variable. It is not. It is a snapshot of a render, and learning to respect that changed how I write components.
My first mistake was mutating state. If state held a list, I would push an item into it and wonder why nothing updated. React compares references, not contents, and if I change the same array in place, React has no clear signal that anything new happened. The fix was to make a new array or a new object every time. It felt wasteful at first, but it is how React knows to re-render, and it keeps the old state intact for comparison.
The second mistake was the stale closure. I would write an event handler or a timer that used a state value, and it would keep using the old value long after the state had changed. That happened because the function had closed over the value from the render when it was created. It was not broken, exactly. It was doing what JavaScript functions do. I just had not accounted for it.
The habit that fixed stale values for me was to think about where the value comes from at the moment of the update. When the next value depends on the previous one, I use the updater form, which receives the latest state instead of relying on whatever the closure captured. For effects and timers, I check dependencies carefully and ask whether the function should be recreated when the value changes.
The third mistake was lifting state too late. I would keep a piece of state deep inside a child component, then discover that a sibling also needed it, or that the parent needed to reset it. By then, the state was tangled up in the wrong place, and moving it meant rewiring several components. Now I ask earlier: who actually owns this value, and who else needs to read or change it? If more than one component cares, the state usually belongs higher up.
Lifting state early is not the same as lifting everything to the top. I have also made that mistake, and it turns a parent into a dumping ground for values it does not really use. The balance I aim for now is simple: keep state as close as possible to where it is used, but no closer than the components that need to share it.
Another habit that helped was to keep state minimal. If a value can be calculated from existing state, I do not store a second copy of it. Two copies will disagree eventually, usually at the worst moment. Derived values belong in the render, not in a separate state variable that has to be kept in sync by hand.
The practical takeaway is a short checklist I now run through: am I creating a new value instead of mutating the old one, does this update depend on the latest state, and does this state live where the components that need it can reach it? Those three questions have fixed more React bugs for me than any clever trick, because they address how state actually works instead of how I wish it worked.
