helix 2022-08-11

Hi folks, long time I don't ask you anything. Hope you are well. I'm trying to use v7 react-hook-form and its register function returns the props to be used by the input. the createElement "input" works because I'm passing a js object straight to props as argument, the (input (register "name")) does not work because its wired to pass nil as props https://github.com/lilactown/helix/blob/master/src/helix/dom.cljc#L172 because the $d macro process props just if they are a clojure map. Wouldn't be the case to accepting (object? props) and pass it to createElement as props without processing instead of nil ?

(.createElement r "div"
                    (.createElement r "label" "ID")
                    (.createElement r "input" (register "id"))

                    (label "name")
                    (input  (register "name")))

I think the trick does not apply here, because I'm not invoking $ (which is create element) and because it is a js object that is returned by register.

I guess I'm just confused why not use $ with &. Pretty sure that works even if the thing returned to & is a js object. Could be wrong though

The $d macro that generates the react-dom call does not expect props other than clojure map.

Hmm the $d macro ought to work the same as $. If not, please file a bug

We started using react-hook-form at work recently. We have a thin wrapper around it. I'll look at it tomorrow

Cannot say that they are different, because $ also ignores js object props https://github.com/lilactown/helix/blob/0ffa64c592aa46bdde8cc24d1ef71cf4cc0bd930/src/helix/core.clj#L57 and I think that is by design.

Yeah but by passing it like {& (register "name")} the argument is a map

So you go down the other path. And the props should all get passed fine.

@jimmy I've tried that {:& (register "name")}

Open a bug with a repro then please. That should work

That works in our app

The whole $ thing is you pass a map and it amasses a js object with a clojure interface. So it just processes props when its called with a clojure map as argument. And that is great because we call ($ ReactSelect {:funkyProperty "string"} and it generates the js object at compile time of that call. If I already have a JS object it treats as children, both in $ and $d the behaviour is consistent with each other, because both assume children, and incosistent with the (if (map? props)) pathway which accepts properties.

that's wrong

or maybe we are misunderstanding each other

($d "div" {:& (register "foo")})
works

just like

($d "div" {:& #js {:on-click #(js/alert "hi")})
works

if you can show a repro for this breaking, you should create a bug

so it can be fixed, because it should work

👍 1

> If I already have a JS object it treats as children this is true if you are passing it bare, without using the "spread props" syntax that @jimmy was saying

($d "div" (register "foo"))
will not work, because it will treat the JS object as a child

That's why I tried initially, because that makes more sense

if I already have a js object why wrap it with a clojure map

See the link that jimmy posted as the first reply in this thread

it's a quirk of helix's syntax to allow writing props as maps normally with as little runtime processing as possible. you can always macro expand the $ to inspect what it is doing

it's not very often that you want to forward all props without some additional overrides, as well. using the {:& (register "foo")} syntax allows you to easily add more props to the element later

Yeah, just tried this out in a test app. It all worked fine for me.

(ns test-react-hook-form.main
  (:require [helix.core :as h]
            [helix.hooks :as hooks]
            [helix.dom :as d]
            ["react-dom/client" :as rdom]
            ["react-hook-form" :as form]))


(h/defnc app []
  (let [form (form/useForm)
        register (.-register form)
        handle-submit (.-handleSubmit form)
        on-submit (fn [data] (println data))]
    (d/form {:onSubmit (handle-submit on-submit)}
            (d/input {:& (register "name")})
            (d/input {:type "submit"}))))
 

(defn init []
  ;; start your app with your favorite React renderer
  (-> (rdom/createRoot (js/document.getElementById "app"))
      (.render (h/$ app))))

the weird syntax for $ is confusing for a lot of people. I onboard a lot of people to it. This kind of case catches people a lot the first time they encounter it

you're not alone geraldodev for not falling into the pit of success, nor for wishing that it worked differently

For what its worth, after using helix for the last few years, I’ve grown to love the $ syntax. IMO very smart tradeoffs of not a lot of abstraction, but also good performance with little to no interpretive overhead.

👍 1

I like $ also, its a createElement on steroids. thank you folks.

when we call register it generates various input props