umbrella icon indicating copy to clipboard operation
umbrella copied to clipboard

[hiccup/hdom] support clj/cljs vectors/maps

Open postspectacular opened this issue 6 years ago • 11 comments

Currently the hiccup/hdom tree normalization stage assumes components are defined using native JS types only (arrays / objects), which precludes idiomatic usage in Clojure/ClojureScript, where one would usually use persistent vectors & maps to define components.

Adding support for these custom types could be done in several ways:

  1. Add additional type checks for namespaced clojure.core.* types / protocols

Pros: trivial implementation Cons: Additional work at runtime, possible performance impact

  1. Port (part of) to Clojure/ClojureScript

Pros: Idiomatic usage, no perf impact (compared to 1) Cons: implementation & maintenance effort

  1. Refactor normalizeTree() / normalizeElement() to inject type checks via IOC / config object

Pros: most flexible (also potentially interesting for other langs) Cons: none really?

Thoughts?

Tweet for reference: https://twitter.com/danpeddle/status/1034706731790729216

postspectacular avatar Aug 29 '18 21:08 postspectacular

For a trial run, it seems to me that the trivial implementation on a branch would be enough to play with and do some kind of discovery of whether it's worth putting in extra work.

The shape of using the library / sharing cljc components etc would be mostly the same regardless of which of the options were chosen?

dazld avatar Sep 04 '18 09:09 dazld

Sounds reasonable, @dazld - I might not have bandwidth to work on this until mid Oct though, but will try...

postspectacular avatar Sep 04 '18 19:09 postspectacular

would it help if I do some stuff on a fork..? if you can point me at where to start on option 1, that would be enough to get going.

current idea for steps to prove it's workable would be something like:

  1. render a bunch of markup on the server with hiccup cljc components
  2. take over rendered markup on the client with a bundle built using the same components and the library
  3. do some data changes and observe it does the right thing

for context - I'm really looking for something that lets us avoid having to pull in react, but still get all the benefits from not applying changes to the dom when they aren't needed, pure (as possible!) components etc.

dazld avatar Sep 05 '18 16:09 dazld

So I was thinking about this a lot over the past few days and was considering to start doing some experiments on this, but then decided to first prioritize the long planned generalization of hdom (#4) to not just work with HTML DOMs, but also other use cases (e.g. scene graphs). There's a kind of pressing need for this right now and having this out of the way (not quite yet, though there's some WIP) will also hopefully give us more guidance and avoid additional refactoring later WRT how this might fit into a CLJS setup.

The first route from above (additional type checks for CLJS vectors/maps) won't be quite enough (due to different APIs used) and so I think to make the most of it, the following functions should be ported to CLJ(S) proper:

  • normalizeTree()
  • normalizeElement()
  • diffElement() & diffAttributes() (incl. a port of @thi.ng/diff)
  • createDOM()

If you still want to have a go (without starting a whole port), then start updating the above functions in the listed order (that's also how the trees are processed). diffElement is little cryptic at the moment, but I've started updating it to make it more legible on the hdom-v5 feature branch...

postspectacular avatar Sep 10 '18 01:09 postspectacular

I got as far as invoking it from CLJS, and clearly, it doesn't know what to do with things like cljs.core.persistentVector as you mentioned - however, when using clj->js recursively (much like reagent / pump etc do..ish) everything works just fine.. at least, so far.

For ref: https://github.com/dazld/hello-hdom/blob/master/src/hello_hdom/core.cljs (using a hand rolled, standalone UMD build of hdom, as couldn't figure out how to make the various cljs toolings directly consume the module, yay).

So, wrapping the hdom api in something that gatekeeps cljs data structures going into it is an option that unblocks your work and my experimentation. If able to directly invoke in the future, cool. The performance will potentially be pretty shonky and unoptimised, but if not doing 60fps dom manipulation, shouldn't be such a big deal.. what do you think?

dazld avatar Sep 10 '18 08:09 dazld

also, starting to think your third option way up at the top is the best one. IOC / injecting knowledge into generalised library seems the cleanest.

dazld avatar Sep 10 '18 08:09 dazld

clj->js works indeed, but really becomes suboptimal for realworld UIs, since you essentially create a copy of every single component, but yeah for starters & not too complex UIs it might be more than sufficient...

Am surprised that there're module import issues, since I thought CLJS can consume CommonJS modules out of the box? Maybe the ES6 syntax causes issues? There's an open issue (#23) about providing ES5 outputs, but I find that whole situation and related complexity hard to understand and justify to spend time on...

In terms of "gatekeeping" CLS types, you should also try out how the cljs->js approach work with components with lifecycle methods. That's a part I haven't thought about yet (or tried out) at all...

The big (thought) wheel keeps on spinning... :)

postspectacular avatar Sep 10 '18 15:09 postspectacular

I'll definitely try the lifecycle stuff - also getting handles on the underlying DOM nodes etc - is there an equivalent for {:ref fn-that-operates-on-DOM} from react land..? In terms of state management, thinking a global atom will be enough for now too.

dazld avatar Sep 11 '18 08:09 dazld

Hi @dazld - just a quick note to say that I'm almost done with the hdom v5 update and now "only" need to update all the related docs. This new version includes an IOC mechanism and various (optional) control attributes to enable branch-local (in the component tree) behaviors. I haven't really had bandwidth to think about the impact on a CLJ(S) version of this, but just wanted to let you know that this isn't forgotten...

Ps. all the new stuff is still on the develop branch only

postspectacular avatar Sep 20 '18 08:09 postspectacular

cool stuff, will take a look when it's baked! do you think this API will be stable for a while..?

dazld avatar Sep 20 '18 15:09 dazld

Once it's released, yes! And even if there're some changes, they should not be drastic, as far as I can tell...

postspectacular avatar Sep 20 '18 22:09 postspectacular