The entire UI is specific to the UI. Trying to separate "entities" or "server data" from "UI concerns" is IMO a waste of time and just confusing.
At the end of the day, you have a state tree, and you project it to a UI. Where that data comes from and what it represents doesn't really matter. Events in the UI generate an log that can get folded into new state to then be projected again. These events could be generated by a button being clicked, or an XHR request completing, but that doesn't change the nature of the payload.
One important thing though, in most of these models, the state shouldn't be "is this menu open or closed". It should be "Is the user focused on an arbitrary task or not". The menu being open or closed is a projection of that. This way when your designer comes and says "No, this thing shouldn't be a dropdown menu anymore, it should be a modal", you don't feel the need to rename your arbitraryMenuOpen: true state key to arbitraryModalOpen: true instead. Saves a lot of time.
Elm is a pretty good example of how all of this works. Its fairly different from Redux and co (even though it inspired it a lot), but its interesting to see how all of these things absolutely can work. They get messy when retrofit into React and JavaScript, but it's not an issue with the concept, just the implementation.
In fact, the reason we have the particular toolbox of standard GUI elements that we have these days is precisely because they serve different use-cases. Each serves a particular one, and the reason it's survived is because it isn't made redundant by any of the others.
The only thing not practical about it is that in these days and age, the amount of people with data modeling experience is low. But it totally does work in practice and is, IMO, the only way to really be productive with Redux. Otherwise any change you make or any new feature requires modifying components, actions, AND reducers. At that point you're just doing 3 times the work for everything.
https://redux.js.org/faq/organizing-state#do-i-have-to-put-a...
Wait, they don't though. That's the whole point of functional components. They are of the form:
const Component = (props) => <JSX />
Where are said "literally passed" actions here?> So there's bi-directional coupling between the UI code and the Store (the UI code renders from the state, and passes messages back to the store).
This isn't bi-directional binding. state determining UI, UI sending queued actions and actions determining state is one-way data flow. Bi-directional coupling would be tying a reference in the state to a UI element.
> Also, conceptually, you often have a lot of state that's very directly concerned with and specific to the UI, and isn't just idyllic, agnostic "data". Things like "is this menu open or closed?" and "what's the current string value of this input (which isn't meaningful until the form is submitted)?"
This is architectural, and is why components have their own state. Its up to you wether you want to keep this as application state or local volatile state.
> In Flux, those things tend to add a ton of boilerplate in terms of change actions, and split tightly-related concerns into separate corners of the codebase (if I remove this menu component I have to remove its open/closed state variable and the corresponding action).
Which is why I don't put it in the main application state (:
> To be sure, there's not really a great way to decouple things in the reactive architecture.
I mean, not if you're dumping everything on your application state, no.
> I personally lean towards stateful components that are decoupled at their boundaries instead, which does carry its own downsides.
Curious to learn what you mean by "decoupled at their boundaries". You mean components that are de-coupled from each other? You can make re-usable, functional or stateful components that are de-coupled from application state.
I think the main takeaway here is that not all state is the same, there's state that's part of your core application logic and state the component needs for its display only. Treating them as the same thing does limit the re-usability of your code and make dealing with your application state tedious.
> The only solution I've ever seen that truly felt right was two-way databinding, which Vue (sort of) still has...
There's other solutions (: I don't think Netflix, Facebook or AirBnB have all the state of their menus tied up with the state of their user data and and have all components be use-once because of that. Good state management can make things scale, and functional components very useful!
> but which has fallen out of fashion in general because it requires "magic" and because it tends to come with performance costs.
Yup. It's also hard to know where or how certain state is changing. It definitely allows you to build small applications very quickly, but it doesn't scale all that well.
Also, conceptually, you often have a lot of state that's very directly concerned with and specific to the UI, and isn't just idyllic, agnostic "data". Things like "is this menu open or closed?" and "what's the current string value of this input (which isn't meaningful until the form is submitted)?" In Flux, those things tend to add a ton of boilerplate in terms of change actions, and split tightly-related concerns into separate corners of the codebase (if I remove this menu component I have to remove its open/closed state variable and the corresponding action).
To be sure, there's not really a great way to decouple things in the reactive architecture. I personally lean towards stateful components that are decoupled at their boundaries instead, which does carry its own downsides. The only solution I've ever seen that truly felt right was two-way databinding, which Vue (sort of) still has, but which has fallen out of fashion in general because it requires "magic" and because it tends to come with performance costs.