> components have to be updated by hand

Most state libraries provide a mechanism by which you can "connect" components to parts of the state you care about, so that they automatically re-render when that part of the state changes. New props come in, and the components can decide what to do with them.

> Or at least have state to be duplicated in them. I mean, if a control/table gets a mutating event, you have to update both outside state and internal setState({input}) as well, otherwise input wouldn’t appear. And when outside state changes by itself, there should be a way to forceUpdate() with props or maybe a direct setState(). Am I right?

So this is where things get murky, regardless of what library you're using.

In the application I work on, our stores generally reflect the source of truth in the backend, and it's real-time so the stores can update literally whenever. This puts two-way data binding you see in other libraries out of the picture. So most of our forms use local state, because we only want the data store to be updated with the final result the end user actually saves. Interaction wise, it wouldn't make sense to submit it anyways until the user blurs it or explicitly saves it (plus it would be unnecessarily expensive to constantly be saving to the backend every keystroke).

And by keeping the state local, if an update comes in from the backend on the entity we're editing in the form, we can notify you that "Hey, someone else updated this!" while still keeping our form state, and letting us make the decision as to whether we want to overwrite it. But we could also choose to just override the form state if we so choose.. we could just look at the "connect"ed state we care about, and if it changes, overwrite the local state in the form. That's generally not desirable though from an interaction perspective.

> In short, you make a shallow component that mounts another entire “app” into its div on its mount, so that updates do not propagate downwards

We do the same thing in our application. Although the top level component technically re-renders when our data store changes (because it's stored in a top level context component), we just have memoized components beneath that take 0 props, which therefore don't re-render even when the parent re-renders.

And we re-use this same pattern through the application, creating separated subtrees that only re-render when data they care about changes.

So I think of our application like this: either user interaction occurs (like a form save), or the backend updates. This changes our data store (which tries to reflect the state on our backend). This update causes the components that care about that specific piece of information to re-render. Their children may choose to re-render or not. And then we wait for more interaction / backend changes. This is where the notion of "unidirectional data flow" comes from - it's basically just a tight one-way loop that drives the presentation of our application. Forms do muck this up a bit with their local state, but that's something I think _every_ application has to think about handling

I will admit I only learned frontend development / React properly at my current company (previous used a messy Backbone.js app), so there might be simpler / better ways at this point, but I find it very easy to reason about our application as a result of this pretty explicit data flow + our architectural constraints.