(ns reagentdemo.news.clockpost (:require [reagent.core :as r :refer [atom]] [reagent.interop :refer-macros [.' .!]] [reagent.debug :refer-macros [dbg]] [reagentdemo.syntax :as s :include-macros true] [sitetools :as tools :refer [link]] [reagentdemo.common :as common :refer [demo-component]] [reagentdemo.news.binaryclock :as binaryclock])) (def url "news/binary-clock.html") (def title "A binary clock") (defn fn-src [src] [demo-component {:src src :no-heading true}]) (defn main [{:keys [summary]}] (let [lexclock {:href "http://www.lexicallyscoped.com/2014/01/23/clojurescript-react-om-binary-clock.html"} hopclock {:href "http://pmbauer.github.io/2014/01/27/hoplon-binary-clock/"} om {:href "https://github.com/swannodette/om"} hoplon {:href "http://hoplon.io"} clocksrc {:href "https://github.com/reagent-project/reagent/blob/master/demo/reagentdemo/news/binaryclock.cljs"}] [:div.reagent-demo [:h1 [link {:href url} title]] [:div.demo-text (when-not summary [:div [:div.clearfix [binaryclock/main]] [:div [:strong "Click to toggle 1/100th seconds."]]]) [:p "Fredrik Dyrkell wrote a very nice " [:a lexclock "binary clock"] " using " [:a om "Om"] ". I thought I’d replicate that using Reagent for fun (another re-write, using " [:a hoplon "Hoplon"] ", can be seen " [:a hopclock "here"] ")."] [:p "So, without further ado, here is a binary clock using Reagent."] (if summary [link {:href url :class 'news-read-mode} "Read more"] [:div.demo-text [fn-src (s/syntaxed "(ns example (:require [reagent.core :as r :refer [atom]]))")] [:p "We start with the basics: The clock is built out of cells, with a light colour if the bit the cell corresponds to is set."] [fn-src (s/src-of [:cell] "reagentdemo/news/binaryclock.cljs")] [:p "Cells are combined into columns of four bits, with a decimal digit at the bottom."] [fn-src (s/src-of [:column] "reagentdemo/news/binaryclock.cljs")] [:p "Columns are in turn combined into pairs:"] [fn-src (s/src-of [:column-pair] "reagentdemo/news/binaryclock.cljs")] [:p "We'll also need the legend on the left side:"] [fn-src (s/src-of [:legend] "reagentdemo/news/binaryclock.cljs")] [:p "We combine these element into a component that shows the legend, hours, minutes and seconds; and optionally 1/100 seconds. It also responds to clicks."] [fn-src (s/src-of [:clock] "reagentdemo/news/binaryclock.cljs")] [:p "We also need to keep track of the time, and of the detail shown, in a Reagent atom. And a function to update the time."] [fn-src (s/src-of [:clock-state :update-time] "reagentdemo/news/binaryclock.cljs")] [:p "And finally we use the " [:code "clock"] " component. The current time is scheduled to be updated, after a suitable delay, every time the main component is rendered (" [:code "reagent.core/next-tick"] " is just a front for " [:code "requestAnimationFrame"] "):"] [fn-src (s/src-of [:main] "reagentdemo/news/binaryclock.cljs")] [:p "The entire source is also available " [:a clocksrc "here"] "."] [:h2 "How it all works"] [:p "Reading through the source, it may look like the entire clock component is recreated from scratch whenever the time changes. "] [:p "That is an illusion: Reagent and React together makes sure that only the parts of the DOM that actually need to change are updated. For example, the " [:code "column-pair"] " function corresponding to hours only runs once every hour."] [:p "And that’s what makes Reagent and React fast. Try clicking on the clock to toggle the display of 1/100th seconds. Most browsers should have no trouble at all keeping up (even if they won’t actually show every 1/100th second: they are typically limited to roughly 60 fps)."] [:p "But it is a very handy illusion. Almost the entire UI is made up of pure functions, transforming immutable data into other immutable data structures. That makes them easy to reason about, and trivial to test. You don’t have to care about ”model objects”, or about how to update the DOM efficiently. "] [:p "Just pass arguments to component functions, return a UI description that corresponds to those arguments, and leave it to React to actually display that UI."]])]])) (tools/register-page url (fn [] [main]) (str "Reagent: " title))