Reagent React Wrapper Components
Table of Contents
1. Overview
Reagent provides a minimalist interface for building React components in ClojureScript, while Re-frame adds an application architecture layer for managing state and side effects.
2. Background
GitHub repo metadata confirms reagent-project/reagent was created 2013-12-18, with Dan Holmsand as the commit author on its early history. day8's own re-frame docs state re-frame "was released in early 2015" by Michael Thompson (copyright notice: "Copyright (C) 2015-2021 Michael Thompson").
Reagent was created by Dan Holmsand in 2013 as a simple way to use React from ClojureScript, representing components as plain functions returning Hiccup-style vectors. Re-frame, developed by Mike Thompson starting in 2015, builds on Reagent to provide a unidirectional data flow architecture inspired by Elm and Redux.
3. Key Concepts
3.1. Reagent Component Forms
Three ways to define components with different lifecycle behaviors:
;; Form-1: Simple function (re-renders on any change)
(defn greeting [name]
[:div "Hello, " name])
;; Form-2: Function returning function (closure for local state)
(defn counter []
(let [count (reagent/atom 0)]
(fn []
[:div
[:span @count]
[:button {:on-click #(swap! count inc)} "+"]])))
;; Form-3: Full lifecycle (for React interop)
(defn canvas-component []
(reagent/create-class
{:component-did-mount (fn [this] (setup-canvas (reagent/dom-node this)))
:reagent-render (fn [] [:canvas])}))
3.2. Re-frame Architecture
Event-driven state management with subscriptions and effects:
;; Event handler
(re-frame/reg-event-db
:increment
(fn [db [_ amount]]
(update db :count + amount)))
;; Subscription
(re-frame/reg-sub
:count
(fn [db _]
(:count db)))
;; Component using subscription
(defn counter-display []
(let [count @(re-frame/subscribe [:count])]
[:div "Count: " count]))
3.3. Wrapping React Libraries
the scoped package "@mui/material" did not exist in 2018 -- it was introduced with MUI v5 in September 2021 (npm registry's earliest published version is 5.0.0-rc.0). The period-correct import on 2018-09-01 was "@material-ui/core" (v3.0.1 was current, published 2018-08-28). Corrected the require path.
Adapt JavaScript React components for Reagent:
(def MaterialButton
(reagent/adapt-react-class
(.-Button (js/require "@material-ui/core"))))
(defn my-button []
[MaterialButton {:variant "contained" :color "primary"}
"Click Me"])
3.4. Component Composition Patterns
;; Higher-order component
(defn with-loading [component]
(fn [props]
(if (:loading props)
[:div.spinner "Loading..."]
[component props])))
;; Render props pattern
(defn mouse-tracker [render-fn]
(let [pos (reagent/atom {:x 0 :y 0})]
(fn []
[:div {:on-mouse-move #(reset! pos {:x (.-clientX %)
:y (.-clientY %)})}
[render-fn @pos]])))
4. Implementation
4.1. Project Dependencies
reagent 1.2.0 and re-frame 1.3.0 postdate this note by years -- neither existed on 2018-09-01. GitHub tag metadata shows the latest releases actually available on that date were reagent 0.8.1 (tagged 2018-05-15) and re-frame 0.10.5 (tagged 2018-02-13; the next release, 0.10.6, landed 2018-09-02, one day after this note). Corrected to the period-accurate versions.
;; deps.edn
{:deps {reagent/reagent {:mvn/version "0.8.1"}
re-frame/re-frame {:mvn/version "0.10.5"}}}
4.2. Application Bootstrap
(ns app.core
(:require [reagent.dom :as rdom]
[re-frame.core :as rf]))
(rf/reg-event-db :initialize
(fn [_ _] {:count 0}))
(defn app []
[:div
[counter-display]
[:button {:on-click #(rf/dispatch [:increment 1])} "+"]])
(defn init []
(rf/dispatch-sync [:initialize])
(rdom/render [app] (.getElementById js/document "app")))
4.3. Effect Handlers for Side Effects
(rf/reg-fx
:http-xhrio
(fn [{:keys [uri on-success on-failure]}]
(-> (js/fetch uri)
(.then #(.json %))
(.then #(rf/dispatch (conj on-success %)))
(.catch #(rf/dispatch (conj on-failure %))))))
5. References
6. Notes
- Use
reagent/trackfor derived computations that cache like subscriptions - Prefer Form-1 components unless you need local state or lifecycle methods
- Re-frame subscriptions are automatically cached and deduplicated
- Consider
re-frame-http-fxfor HTTP effects rather than custom implementations - Use
re-frame-10xdevtools for time-travel debugging in development
7. Related
- Getting Started with Reagent – a greenfield project with these components
- Reagent Development Tooling – builds and hot reload
- Q1 2016 Review of React and Flux – the React underneath