This is great.
What I like about the clojure/script community is that a library like yours helps me understand how to program at a lower level and makes it possible to build my own stack of libraries that I know inside and out.
I’d been wanting to replace React since it’s the last one in my stack that had been a blackbox for me. When I saw this post a few days ago while researching about virtual doms, I jumped on it and have spent many hours reading DOM APIs, Snabbdom, Quiescent, and your library. While I’ve neither fully comprehended nor spent any time actually using any of them, I want to share my current thoughts before they sink into oblivion.
First off, I really like the idea of using snabbdom to implement a purely functional architecture of Quiescent. I already have my core.async based state management library, which I currently plug into Rum components through its reactive mixins, and getting rid of component states would make separation of concerns much clearer. I found snabbdom simple and elegant. It’s a full circus of mutation and looping but I could at least follow the underlying logic based off of DOM APIs.
Then I tried to cross a bridge back to the clojurescript world and failed. I fell off. I feel like I got lost in semantics. With its imperative algorithm, snabbdom is complex enough that I could barely wrap my head around. But its language closely follows that of DOM APIs so I could at least understand when and where DOMElements get created, inserted, updated, destroyed, and removed. On the other hand, dumdom seems to emphasize its compatibility with Quiescent, which, if I understand correctly, draws its terminology from React. I had hard time translating dom lifecycle events between them and figuring out the same when and where of will-appear, did-appear will-enter, did-enter, etc. It’s not that explanation is lacking or code is hard to read, but that it was cognitively too much after coming all the way up from DOM APIs. Maybe this is a matter of getting used to but I personally felt the terms based on DOM APIs are easier to follow.
Second, how would you explain the comp-fn that defcomponent returns, if I ask you to rephrase it in a more descriptive manner? If I understand it correctly, dumdom puts an extra step on top of what snabbdom does. In the latter, you write hyperscript to create vnodes that contain all necessary data for real nodes. In the former, you write hiccup in place of hyperscript to return this comp-fn, which takes path and key to create vnodes. I’m wondering how important it is to add this extra step in your implementation of Quiescent APIs using snabbdom vdoms.
Finally, in dumdom.core/init-node!, why do you avoid replacing the target dom with a new one? I have at least noticed that holding on to the original element is useful in testing.
I hope I don’t come across overcritical. I just want to better understand what is going on at this low level of programming. I’ve learned a ton from reading your code and I thank you for that.