When you’re building a frontend with Om, it gives you a couple of ways to store data. Fundamentally, Om mediates between data (which the user can’t see) and the DOM (which they can). Because of this, it’s important to think about where you keep the data.
The easiest place to store data is in the application state. This is something like a set of global variables. It lives at the very top level of the application. What happens is that the top level DOM object has access to it, and so do all its children. When you build an Om component (
om/build), you can pass some or all of the application state into whatever component you’re building and that component will have access to it also.
But you don’t always want every component to have access to the application state because when the application state changes, Om has to render (or decide whether or not to render) everything that depends on it. You want to separate concerns, not only for programming best practices, but because you don’t want to waste system resources. Even though Om won’t be rebuilding the actual DOM every time the application state changes, it will be running functions like
IWillUpdate, etc., and if we were able to keep our concerns separate, it wouldn’t have to.
Om provides something called component state which you define in
IInitState and access with
om/update-state!. This is the best place to store your various stuff that deals with user input: event handlers, text input, screen size, whatever. Not every component in your application needs to re-render on mousemove — but maybe one of them does. And you need to do
removeEventListener on exactly the same function, or
removeEventListener won’t work and you’ll crash the browser with infinite
mousemove event listeners. That function needs to live somewhere, and component state is that place.
What we have is a very top-down model: everything gets what it needs from its parent, and nothing more, and everything takes care of its own business. Application state holds only data that is truly global to the application. If data is unique to some component, that component holds it. Basic encapsulation, right?
But what if you want to pop up a modal window when the user clicks a particular button?
We have a website with several side- and top-bar style menus, which for their own reasons need to be in a layer on top of the main page content. The main page content has any number of children, and nested 3 or 4 levels down, a descendent of these children, is our component. The task is to create a modal window that is drawn on top of all this, and which is controlled by user input to our component.
The rule is that z-index only negotiates between children of the same parent (except sometimes, apparently, in the heterodox Safari). Now, recall that our component is a descendent of the main page area. Since the main page area is under the side menus, all of its children have to be under the side menus. They’re not in the same stacking context (look that term up if z-index has ever mystified you!). It just can’t be done. This means that the modal window has to be a sibling of the menus and the main page content, which means it has to live near the top of the DOM, along with the other high level structural elements.
This is the slight difficulty we’ve been having with Om: That sometimes a component way down deep in the DOM needs to control the behavior of a component higher up in the DOM, and it can’t without using the application state.
Also, you can’t mutate application state inside of
IWillUnmount, which causes problems when you want your modal window to disappear whenever its logical (but not DOM) parent disappears. That’s a whole other can of worms, involving when the render phase happens, but it’s another instance of the problems that happen in Om when a component’s logical parent is different from its DOM parent.
Here is what we ended up doing to solve this problem. If somebody has found a more elegant way for components that are distantly related in the DOM to communicate without using the application state, we’d love to hear about it! Let us know at firstname.lastname@example.org Or join the conversation on twitter:
— Altometrics, Inc. (@Altometrics) April 20, 2015