Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the difference between application state and component local state in Clojurescript Om?

As I understand it:

Application state is the "global" state that all components in the component tree can access, through cursors. This is the state that your application is in and basically what is being rendered by Om. So, for example, if you are writing a chat program, the application state would contain a list of users in the conversation and all of the messages that have been sent, or whatever.

Component local state is state that is local to a single component and cannot be seen outside of this component. It is set either by passing {:init-state } to build, or by implementing IInitState and returning a map from init-state - or both (in this case, they are conj'd together). David Nolen recommends that local state should only be used for transient state, such as if the mouse is currently pressed in a drag/drop component and that all other state should be application state. That is, if you have a tab widget, the currently selected tab should be set in application state (not local state!), but if the tab is being dragged to a new location, the current position and mouse state would be (temporarily - until the drag operation is complete) be stored in component local state. Things like core.async channels can also be stored in local state (though I've also stored them (and seen others do the same) in shared state and additional data - see below for details on both)

Cursors only apply to application state and are like windows into it so that components far down the tree can access only the data that they actually need to access.

Application state is always accessed through a cursor (app in the tutorial) and modifying application state is done through a cursor - both om/update! and om/transact! take a cursor as their first argument. You can also set the application state atom directly with reset! and swap!, but David recommends against this as by doing that you lose out on some of Om's more advanced features (like being notified of change deltas).

Local state can be received through IRenderState or by accessing it directly with om/get-state. You can set local state with om/set-state! and om/upate-state!. All three of these take a component backing object (owner in the tutorial).

There is also a third type of state in Om: shared state. Shared state is passed to om/root using the {:shared ...} option and can be accessed from any component in the tree under that root, using om/get-shared. The difference between this and application state is that application state is narrowed down through cursor paths - that is, sub components may not have access to the entire application state - while shared state is always accessible. Also, modifying application state causes the component to be re-rendered while shared state does not trigger renders.

As an aside, there is actually a fourth type too - you can pass additional data to components through build using the {:opts ...} option. This is data that lives outside of the Om/react lifecycle - that is, its immutable data that you can access from a component, but the component does not manage it in any way. This seems to be most useful for configuration data.