5

Om, the clojurescript wrapper of React, is said to be very fast because it leverages immutability. I can't understand how persistent data structures can help here.

What I have understood is that application state is an atom. This state is passed to functions (om components) that return virtual DOM nodes, so that creating a "patch" of the differences between the current virtual DOM and its previous state is much better then operating directly on the actual DOM.

But where persistent data structures can help here?

(def app-state (atom {:foo {:counter 0}))
(om/root click-counter app-state {:target ...}) 

for example click-counter render a button that when clicked increments the counter. So The transition function looks like this:

(dom/button #js {:onClick #(om/transact! app [:foo :counter] inc)} 
            (str "clicked " (-> app :foo :counter) " times"))

I undestand this: when onClick is executed clojurescript creates a new map (very efficiently) like this:

{:foo {:counter 1}}

and app-state now points to the new map. At this point Om is aware that the state is changed because it's just a matter of one equality check.

The problem here is that Om should still calculate the difference between the whole old virtual DOM and the new one. It doesn't know that just the counter is changed.

Where is my mistake?

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
agori
  • 481
  • 6
  • 14

1 Answers1

4

When the app state is stored in a persistent tree structure like a map it's immediately obvious which parts of the state tree didn't change, and don't need updating. This is because any change to a child changes the parent. With mutable data structures changes to the children don't have to change the parents.

So if your state looked like this:

{:part1 [1 2 3 4]
 :part2 [:a :b]}

and you make a new state by adding something in part2:

{:part1 [1 2 3 4]
 :part2 [:a :b :c]}

then the comparison function can look and see that the value in :part1 of the old and new state is the exact same object and therefore cannot have any changes to any nested state because it's immutable.

Arthur Ulfeldt
  • 90,827
  • 27
  • 201
  • 284
  • Thanks. So only if an equality check on a node fails than an eventual re-render of its children is required. – agori Jun 08 '15 at 22:07
  • 1
    If equality fails then it is possible a rerender is required, though maybe not, you could have changed it and then changed it back. If equality passes then you absolutely *don't* need to update it. – Arthur Ulfeldt Jun 08 '15 at 22:49
  • The problem I see is that with big flat data structures like a vector of thousands of elements (maybe an excel-like application) and you change just one element of the vector: `(def cells (atom (into [] (replicate 1000 {:val :empty}))))` and then `(swap! (assoc-in cells [1 :val] 10))`. This causes Om to check all the elements because the root of the data structure is changed and you don't know which cell has been modified. So it seems that everything is quite dependent on how you decide to structure you application state, given that Om doesn't perform well with big flat data structures. – agori Jun 09 '15 at 07:59