Complex state usually starts as a clean object and then slowly turns noisy as edge cases grow. The biggest improvement for me has been naming state transitions clearly.
I keep update helpers small and intentional. If an update path starts to feel magical, I split it into an explicit function and make the call sites boring again.
Another useful habit: keep view-only transforms out of state whenever possible. Derived data should stay derived so the source remains trustworthy.
None of this is fancy. It just keeps components readable three weeks later when context is gone.