nosco / hx

A simple, easy to use library for React development in ClojureScript.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Why shallow-clj->js?

garrett-hopper opened this issue · comments

It would be nice to not have to call clj->js myself when passing in values to React components.
I assume shallow-clj->js is just a performance optimization? How significant is it?
I've found myself several times now wanting to pass a Clojure object into a React component and having to wrap it in a clj->js.

hx makes no distinction between components defined in CLJS and components defined in JS.

If a component accepts JS objs/arrays for certain properties, you'll need to ensure you pass them in as such.

Otherwise every component defined in hx would need to recursively convert all props to CLJS structures (or use interop). You would lose metadata and benefits like easy memoization of components.

If you're doing quite a bit of interop, you could create a wrapper component that calls cljs->js for you, or even an HOC, to cut out the boilerplate.

So even for CLJS components, their props are passed through shallow-clj->js and then shallow-js->clj for the component to use? I'm a little confused by this code. I went ahead and changed hx.react/props->clj to use js->clj and hx.utils/clj->props to use clj->js, and it fixed my problem, but I can see how that's sub-optimal when using CLJS components (losing metadata and whatnot).

Yep, even for CLJS components, their props are passed through shallow-clj->js when parsing hiccup. defnc converts the props object to a map for you.

See: hiccup.cljs#L39, utils.cljc.

This is why you can use React components and CLJS components with exactly the same syntax; there is no distinction between them when parsing hiccup, and defnc converts the props from a JS object to a CLJS map for you for your convenience.

I don't see this as a problem; it is a tradeoff. Most components in CLJS projects are written in CLJS; hx goes farther than other libraries to help with interop with plain React components, but deeply converting props to CLJS maps and back is just where I want to draw the line.

If it helps, here's an HOC you can use to skip some of the boilerplate:

(hx.react/defn with-edn [c]
  (fn WithEdnHoc [props]
    (hx.hiccup/make-node
     c
     (-> props hx.react/props->clj clj->js) ;; deep conversion to JS
     (.-children props)))) ;; pass in children separately

So in something like reagent this is explicitly only done when using :>, right?
I'm trying to think if there's a clean way to use both CLJS and JS components interchangeable without any sort of ugly wrapping of the component or clj->js type work on the user's side.
This issue can be closed; thanks for talking it through with me, @Lokeh.

Yeah, I've thought about ways to "tag" components that were made with hx to differentiate them so I could do more optimizations. But it's always going to be leaky.

At the end of the day, only the dev can know what kind of props a components needs at runtime in a dynamic language like CLJS.

Just my 2 cents. It bothered me a little at the beginning, mostly for "ideological" reasons. Now I am more happy with this approach, cause the interop with js stuff is so much easier.