Hot reloading Custom Elements (Web Components)

I was strugling to make Custom Elements hot reloadable as the CustomElementRegistry does not allow redefinitions for a single tag. It’s basically a follow up to the topic how-to-create-custom-elements-web-components-with-clojurescript that I thought was worth documenting here.

If you call CustomElement.define twice for the same tag it’ll throw the error

Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry': the name "k-component" has already been used with this registry

Here is my solution.

(defonce already-defined-components (atom {})) ;; This is used for hot reloading.  
(defn webcomponent! [name view-component]                                          
  (if (js/window.customElements.get name)                                          
    (swap! already-defined-components #(assoc % name view-component))              
    (let [set-shadow #(do (set! (.-shadow %) (.attachShadow % #js {:mode "open"}))#
          _ (swap! already-defined-components #(assoc % name view-component))      
          render #(do (reagent.dom/render [(@already-defined-components name)] (.-#
          ;; defines the constructor function, which is the "class" object used by#
          component (fn component []                                               
                      (-> (js/Reflect.construct js/HTMLElement #js [] component)   
                          set-shadow                                               
                          render))]                                                
      (set! (.-prototype component)                                                
            ;; establishes prototype hierarchy                                     
            (js/Object.create (.-prototype js/HTMLElement) #js {}))                
                                                                                   
      ;;finally, defines the component with these values                           
      (js/window.customElements.define name component))))                          
                                                                                   
(defn m-component []                                                               
  [:div                                                                            
   [:style ;; Scoped style                                                                         
    "* {color: blue;}"]                                                            
   [:h1 "Ex 11"]])                                                                 
                                                                                   
(webcomponent! "k-component" m-component)                                          
                                                                                   
(defn my-page []                                                                   
  [:k-component])
2 Likes