1

I'm getting started with om and ClojureScript with a very simple application.

My global app-state looks like this:

(def app-state (atom {:animals [ {:name "dog" :img "pic01.jpg"}
                                 {:name "cat" :img "pic02.jpg"}
                                 {:name "mouse" :img "pic03.jpg"}
                                 {:name "camel" :img "pic04.jpg"}]}))

The name property of each hash-map inside the vector "animals" is rendered to a HTML list structure (i.g. LI-tag) by an om component that I called "menu". Each entry is rendered by another component called "entry". (i.g. as UL element). Whenever the user hovers one of the list entries the appearance of the entry changes (maybe the background changes). I capture these current states inside the entry component, which I initialize via om/IInitState.

This works so far. Now I want to add another component, which is called "display". It should be capable to display the images which are associated to the animal names in the global state whenever the user clicks on an entry in the list. I'm asking myself what would be the best way to solve this.

I see two possibilities:

Keeping a local state in the "display" component, which is updated by an onClick event from the "entry" component. Here, my question would be: How can I update an component's state from another component?

Or introducing another property in the global state, maybe called: "active_section", which is updated by the onClick event in the entry component and read by the "display" component. But is that necessary?

Anton Harald
  • 5,772
  • 4
  • 27
  • 61

2 Answers2

2

Not sure I understand your requirements 100%, but will throw in my 2 cents worth.

In general, I would avoid a design which involves one component somehow updating local state in another component. This would create tight coupling between the components, which is probably not something you really want. Far better off to design/develop your components so that they have as little, if any, knowledge about other components.

The best approach with react based apps is to keep your state in the global state atom. In this case, I would have one component (the menu component?) set a :visible or :current property value in your global state which indicates the current animal. Your other component would render the animal based on the entry with this property set to true.

The other advantage of doing it this way is that should you decide to add additional components which need to operate on whichever 'animal' is current, it can also use that information.

Keeping all state change which may trigger DOM rendering in one place can also make debugging easier. Also, check out om-next (there is a quick start tutorial) as it has a new approach for setting/geetting state which promises to make things a lot simpler than the old 'cursor' approach.

Tim X
  • 4,158
  • 1
  • 20
  • 26
  • Thanks Tim, this was exactly the sort of consideration I was looking for. very helpful. om/next will be the _next_ thing I check out. but step by step. – Anton Harald Dec 29 '15 at 07:44
1

I use two different approaches. The first is to have a component "broadcast" it's state as a message. For instance, I have a calendar component that "broadcasts" it's "current date". When someone clicks on a different date, it broadcasts that new date. Any component can listen for that message and update itself as it feels is necessary.

My second approach is for components to broadcast messages "to another" component. It does this by broadcasting a "type" of message that the other component is listening for. For instance, I have a component that wants to edit a customer. It broadcasts the "customer" it wants to be edited to a "Customer Edit Component". The "Customer Edit Component" decides if it can display the Customer or not and handles the editing. If it decides to save changes the "save" message is broadcast. Something listing for "save" messages processes the request and broadcasts the result of the "save". Components who display customer information process the "updated" customer message and display the changes.

The only coupling is between the message bus and the messages. All of this is implemented using core.async.

The question that you should decide upfront is whether or not to have more than one bus (channel). The other issue is that the components need to short-circuit messages not directed to them.

I have a special component that listens to ALL messages for debugging purposes.

I would be careful about tying all of your global data directly to components. I prefer to think of the global data as a database, not the internal data of a component.

Chaos Rules
  • 410
  • 1
  • 3
  • 14