Fork me on GitHub
#reagent
<
2023-03-06
>
Valentin Mouret14:03:58

Hello 🙂 In new versions of reagent, I get a warning that reagent.dom/dom-node is deprecated, however the documentation does not say what to replace it with. My use-case: I have a very simple component that’s a code block <code></code>. On mount of this component, I want to call highlight JS’ highlightElement. I have code which looks like this:

(defn highlight-element
  [el]
  (.highlightElement hljs (reagent.dom/dom-node el)))

(defn code-block
  ([s lang]
   (reagent/create-class
    {:reagent-render       (fn [s] [:code (when lang {:class lang}) s])
     :component-did-mount  highlight-element
     :component-did-update highlight-element})))
Can someone please help me figure this out?

p-himik14:03:56

You should replace that function with React refs.

p-himik14:03:46

Note also that :component-did-mount receives not el but this - an instance of the component itself and not its corresponding DOM node (which might not even exist).

Valentin Mouret14:03:35

Thanks for the info and quick reply! On to learning about React refs then.

p-himik14:03:42

In this case, it'll be something as simple as:

;; The exact `:require` vector might be different, depends on your build tool.
;; I'm using only shadow-cljs - this works there just fine.
(require '["react" :as react]
         '[reagent.core :as r])

(defn highlight-element
  [el]
  (.highlightElement hljs el))

(defn code-block
  ;; Note that I moved `lang` to the render fn since otherwise changing the arg would not cause a re-render.
  [_ _]
  (let [ref (react/createRef)]
    (r/create-class
      {:reagent-render       (fn [s lang]
                               [:code {:ref   ref
                                       ;; No need for `when` here, even if `lang` is `nil`.
                                       :class lang}
                                s])
       :component-did-mount  (fn [_this]
                               (highlight-element (.-current ref)))
       :component-did-update (fn [_this _old-argv _old-state _snapshot]
                               (highlight-element (.-current ref)))})))

👍 2
Valentin Mouret14:03:32

Is there a reason why I should not do this?

(defn highlight-element
  [el]
  (.highlightElement hljs el))

(defn code-block
  ([s]
   (code-block s nil))
  ([s lang]
   [:code
    {:ref   (fn [el] (when el (highlight-element el)))
     :class lang}
    s]))

Valentin Mouret14:03:40

Ah, I guess the ref callback is called twice, so sometimes el can be nil, which causes the call to highlight JS to fail. As far as I understand, I do not need the :component-* callbacks anymore.

Valentin Mouret14:03:32

Looks like it’s working like a charm on my end. 🙂 I only removed code 🥳

juhoteperi15:03:20

Ref fn is called when the component is mounted or unmounted, which might not happen always when s changes. So it is possible the highlights aren't always updated when the code changes.

juhoteperi15:03:08

One option would be to use useEffect hook to trigger the call when ref or code changes:

(let [ref (react/useRef)]
  (react/useEffect
      (fn []
          (when (.-current ref)
             (.highlightElement (.-current ref)))
         js/undefined)
       #js [(.-current ref) s])
   [:code ...])
Something like this.

juhoteperi15:03:59

Or useEffect without the dependencies would run after every render similar to componentDidMount + componentDidUpdate