All logic runs in Web Workers — the DOM is proxied.
(ns reagami-counter.core (:require-macros [cljs-thread.core :refer [future in spawn]]) (:require [cljs-thread.core :as t] [reagami.core :as reagami] ["d3" :as d3] ["canvas-confetti" :as confetti])) ;; we're in a worker right now... (defonce state (t/atom ::state {:counter 0})) (defn update-bar! [n] (-> (d3/select "#bar") (.transition) (.duration 300) (.attr "width" (str (* n 10) "%")))) (defn my-component [] [:div [:svg {:width "100%" :height 40} [:rect#bar {:x 0 :y 5 :height 30 :fill "#4CAF50" :rx 4 :width 0}]] [:div "Counted: " @(future (* 100 (:counter @state)))] [:button {:on-click #(let [n (:counter (swap! state update :counter inc))] (update-bar! n) (when (= n 10) (confetti #js {:particleCount 200 :spread 70})))} "Click me!"]]) (defn render [] (reagami/render (.querySelector js/document "#app") [my-component])) (def renderer (spawn ::renderer (add-watch state ::render (fn [_ _ _ _] (render))))) (defn ^:export main [] (in renderer (render)))
<div id="app"></div> <script>if(typeof importScripts==="undefined"){importScripts=function(){var xhr=new XMLHttpRequest();for(var i=0;i<arguments.length;i++){xhr.open("GET",arguments[i],false);xhr.send();if(xhr.status===200)(0,eval)(xhr.responseText);}}}</script> <script src="cljs-thread.js"></script> <script src="screen.js"></script> <script>cljs_thread.main(reagami_counter.core.main)</script>