It’s not that it doesn’t work, it does.
But I’ve found there are basically two scenarios:
1) Simple cases where there’s no risk of contention and you can just operate as if your state variable is up-to-date.
2) Complex cases where you have state changes coming from multiple directions and contention is a concern.
In the second scenario I just go straight to useReducer. It is much more reliable than useState when there is the possibility of contention. And it’s much easier to reason about than having a bunch of setState calls all over.
IMO the setState((oldState) => newState) pattern is a half measure that’s almost never the best choice.