An Om question: what do you store in the app state and what do you store in component local state?
In my own application, I started off storing everything in app state, using local state only for short-lived transient data, but over time I found that having a bunch of information like what tab is currently visible in the app state became difficult to manage and made the app state a nightmare. So now I like to keep a strict separation: app state is basically my model - the data being processed (if I were writing a text editor, its the document being edited) and local state is everything else: which dialog is open, which tab is selected, button toggle state, mouse state etc (in a text editor, whether bold is selected, whether I'm in print preview mode or edit mode, etc). Basically app state is what is being processed and local state is how to process or display it. This separation also means that the app state is what gets synced with the server. I like to think of it as "if its something I might want to persist in the database, then it lives in app state, if its something I would never want in the database, then it lives in local state"
I'm curious what other peoples experience with this is.
Another Om question: how do you sync state between client and server, or how do you handle communication?
I looked at how om-sync does this (using tx-listen to automatically sync changes), but ended up building a more traditional request/response model using sente[0] and having my app ask for sub-trees of my app state as needed. Again, just wondering about peoples experience with this and different approaches tried.
We're still figuring out exactly where the boundary lies, but our current approach is much like yours -- view state that's purely local to a component goes in local state, and everything else goes in app state. This feels more natural, but means you lose some of the nice benefits of things like replay/undo for this local state.
For client/server, we've started building up some nice abstractions around our API (like fetching more data in a generic paginated list stored in a cursor), but the write side is still mostly manual. We're trying to use the same API for iOS and web, which probably prevents us from doing some fancier things that continue the same architecture back into the server.
So far, I've only made some (large-ish, granted) prototypes with Cljs and Om, but I've run into this as well. I ended up opting out of local state. I put view state and model state in the same atom, but I tried my best to keep them separate within the atom.
Easy replay/undo was one reason. Another reason was that I wanted to be able to take a _complete_ snapshot of the app state, for debugging purposes. A third was that I wanted to keep all the state that a component needs to know in order to render itself in one place, not many places.
I put viewmodel state with the app state - my app state basically is viewmodel state, domain state source of truth is the database really, so you need to do ajax to traverse the domain object graph, its not stored on the client.
Just like you want to control a react.dom.input, you also may want to control a tabstrip. Maybe you have some biz logic that says "When you click save, select the next record, and if record.type===2, select the third tab". You never know in advance what state needs to be controlled from the top, I found myself refactoring my viewmodel state higher and higher.
The mouse state is never going to be controlled from the top, so that is component local state.
It sounds like you're differentiating between model state and view model state.
I haven't used Om yet, but a potential approach could be to maintain two stateful objects: one for the model (e.g. stuff that you'd persist permanently), one for the view model (e.g. which tab is selected).
If you wanted your app to come back to exactly the same state after a page reload, you could persist the model in databases, and the view model in browser local storage.
In my own application, I started off storing everything in app state, using local state only for short-lived transient data, but over time I found that having a bunch of information like what tab is currently visible in the app state became difficult to manage and made the app state a nightmare. So now I like to keep a strict separation: app state is basically my model - the data being processed (if I were writing a text editor, its the document being edited) and local state is everything else: which dialog is open, which tab is selected, button toggle state, mouse state etc (in a text editor, whether bold is selected, whether I'm in print preview mode or edit mode, etc). Basically app state is what is being processed and local state is how to process or display it. This separation also means that the app state is what gets synced with the server. I like to think of it as "if its something I might want to persist in the database, then it lives in app state, if its something I would never want in the database, then it lives in local state"
I'm curious what other peoples experience with this is.
Another Om question: how do you sync state between client and server, or how do you handle communication?
I looked at how om-sync does this (using tx-listen to automatically sync changes), but ended up building a more traditional request/response model using sente[0] and having my app ask for sub-trees of my app state as needed. Again, just wondering about peoples experience with this and different approaches tried.
[0] https://github.com/ptaoussanis/sente