But on a complex app, to see "dirty bitflags", "intermediate values", "redrew the UI in the middle of mutating document state", I feel what you feel, and I want to share what helps me and my team overcome this kind of complexity.
This happened a few years ago to my team. We alleviated this with several solutions. The most effective solutions is to 1.) separate UI logic/compositive component from UI visual/appearance component, 2.) separate UI component from actors.
1.) Separating logic from visual helps developers define problems separately, especially those around the required states, step-functions, and lifecycle. We made this separation because of three things: 1.) We observed that our programmers are simply divided into these two categories, 2.) The dependencies of these two sets of components are simply different (e.g. logic --depends-on-> actors and ownership/lifetime management modules, while visuals depends on DOM, rendering, setting up callbacks), 3.) It is actually good to have a high cohesion between UI and logic modules. You may want to use the same UI for a slightly different logic, and vice versa.
2.) Separating UI component from actors helps a lot with decoupling state changes and re-rendering, and actually gives developer a chance to separate/abstract/generalize concerns when things have gotten too complex to be written in a UI component.
Actors is what I call any kinds of data that can act, have their own agency. It can be what you call a service, worker. It can have a Timer-based or queue-based internal lifecycle-management that does not require interaction from user (very useful for things like updating background data at interval)
UI component can both "own" or "borrow" actors. "owned" actors dies when its parent component is destroyed, while "borrowed" actors does not die when its borrowing component is destroyed.
We decouple actors from UI component and set up bridge between them. UI component can signal actors by function call, and actors can get back to the UI component by using either a return value or event/callback system.
And the last but not least, writing app in a smartly typed language combined with functional domain modelling helps a lot (see about it here https://www.youtube.com/watch?v=Up7LcbGZFuo).