Fork me on GitHub
#helix
<
2023-09-12
>
Sergio21:09:46

Hi, I'm starting with Helix and would like to know how to convert this to cljs:

import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { StyledEngineProvider, CssVarsProvider } from '@mui/joy/styles';

ReactDOM.createRoot(document.querySelector("#root")).render(
  <React.StrictMode>
    <StyledEngineProvider injectFirst>
      <CssVarsProvider>
        <Demo />
      </CssVarsProvider>
    </StyledEngineProvider>
  </React.StrictMode>
);
I've tried:
(ns app.example
  (:require [helix.core :refer [defnc $]]            
            ["react" :as react]
            ["@mui/joy/styles" :refer (StyledEngineProvider CssVarsProvider)]))

(defnc app []
  (StyledEngineProvider ; fail: not a function nor a component
    (CssVarsProvider    ; fail: not a function nor a component
      ($ Box
         ($ tabs)))))

;; Start your app with your favorite React renderer
(defonce root (rdom/createRoot (js/document.getElementById "app")))

(defn ^:export init []
  (.render root ($ app))
  (js/console.log "Running!"))

hifumi12321:09:16

Use $ to render components

Sergio21:09:55

thank you for the info, my main problem is not to render components created with defnc but that

(StyledEngineProvider ; fail: not a function nor a component
    (CssVarsProvider    ; fail: not a function nor a component
are not components 😞

Sergio21:09:03

wait, it works! 😐 I must be tired!

hifumi12321:09:34

Glad to hear it was fixed 🙂

hifumi12321:09:12

(ns app.example
  (:require [helix.core :refer [defnc $]]            
            ["react-dom/client" :as rdc]
            ["@mui/joy/styles" :refer [CssVarsProvider StyledEngineProvider]]))

(.render (rdc/createRoot (js/document.querySelector "#root"))
  ($ StyledEngineProvider {:injectFirst true}
    ($ CssVarsProvider
      ($ Demo))))

1
hifumi12321:09:14

sth like this should work

dvingo22:09:27

I thought it would be useful to see in the browser elements panel what dom nodes map to which helix components was curious if there is interest to make a PR to add the feature to helix (see thread for code)

dvingo22:09:11

#?(:cljs
   (defn forward-ref? [c]
     (and (object? c)
       (object? (g/get c "type"))
       (=
         (g/get (g/get c "type") "$$typeof")
         (. js/Symbol for "react.forward_ref")))))

#?(:cljs
   (defn dom-node? [c]
     (or
       (and (object? c) (string? (g/get c "type")))
       (forward-ref? c))))

#?(:cljs (def component-name-attribute "data-helix-component"))

#?(:cljs
   (defn apply-data-attr-to-first-child [component-name children]
     (let [c (hchild/map
               (fn [child]
                 (cond
                   (not (react/isValidElement child)) child

                   (dom-node? child)
                   (react/cloneElement child (js-obj component-name-attribute component-name))

                   (g/getValueByKeys child "props" "children")
                   (let [children1 (g/getValueByKeys child "props" "children")]
                     (react/cloneElement child #js{} (apply-data-attr-to-first-child component-name children1)))

                   :else
                   child))
               children)]
       (if (= (hchild/count children) 1) (first c) c))))

#?(:clj
   (defn- fnc* [display-name props-bindings body]
     (let [component-name (str *ns* "/" (str/replace (name display-name) "-render" ""))]
       `(fn ^js/React.Element ~@(when (some? display-name) [display-name])
          ;; maybe-ref for react/forwardRef support
          [props# maybe-ref#]
          (let [~props-bindings [(helix.core/extract-cljs-props props#) maybe-ref#]
                children# ~(if body `(do ~@body) [])]
            (if ^boolean goog/DEBUG
              (apply-data-attr-to-first-child ~component-name children#)
              children#))))))
comes down to augmenting helix.core/fnc*

dvingo22:09:19

and the result of using it:

hifumi12323:09:48

iirc React DevTools already has similar functionality. Try selecting a DOM node then clicking the “Components” tab

dvingo23:09:41

yes, this is for the dom, not react

lilactown04:09:03

I wouldn't accept this PR. React DevTools can easily go the other way, and I don't want to litter peoples apps with these extraneous data-helix-component attributes

lilactown04:09:38

it seems neat, and if you like to use it in your app, all the power is yours!

😎 1