Create, Read and Delete UI
Integrate Reagent and Reframe to create UI for listing and deleting graphics.
Create, read and delete UI#
With the functionality in place, we will now build the user interface for creating, reading, and deleting graphics. Here's the wireframe we started with:

Title and create button#
The page title "Graphics" and the Create button can be clubbed into a component called header
:
(defn header []
(let [creating-graphic? @(rf/subscribe [:app.domain.firebase/creating-graphic?])]
[:div {:class "flex justify-between my-4 mx-auto"}
[:div.text-xl "Graphics"]
[:> Button {:text "Create new" :intent (.-PRIMARY Intent) :loading creating-graphic?
:on-click #(rf/dispatch [:app.domain.firebase/create-graphic])}]]))
The header
component renders a :div
for the title and a Blueprint Button
.
The call-to-action button dispatches the :a.d.f/create-graphic
action, similar to what we did using the REPL. The component also subscribes to :a.d.f/creating-graphic?
and uses the subscription value as :loading
prop to the Button
component. We have not written this subscription yet.
For this code to work, we need to require
the necessary libraries:
(ns app.pages.graphics
(:require [app.components.nav :as nav]
[app.components.auth :as auth]
[re-frame.core :as rf]
[reagent.core :as r]
["@blueprintjs/core" :refer (Button Intent Spinner Card)]))
Subscription#
We can pull the ::creating-graphic?
flag from app-db
using a subscription in a.d.firebase
namespace. We defined this flag when we wrote the handler for ::create-graphic
event:
(rf/reg-sub
::creating-graphic?
#(-> % ::creating-graphic?))
If you find the usage of the key repetitive, you can use the reframe-utils library. It provides a wrapper to Reframe API and allows for concise event handlers, effects, and subscriptions. With reframe-utils
, the above subscription can be written as:
(rfu/reg-basic-sub ::creating-graphics?)
;; which is equivalent to
(rfu/reg-basic-sub
::creating-graphics? ; sub id
::creating-graphics? ; db key
)
;; which is equivalent to
(rf/reg-sub
::creating-graphic?
#(-> % ::creating-graphic?))
Render header#
We can add this component to page
component so it renders on /graphics
route:
(defn page []
[auth/private
[nav/top]
[:div {:class "w-100 md:w-2/3 m-auto p-4"}
[header]
]])
If everything worked well, you'll see the header component on the /graphics
route. Clicking the Create New
button will create a new graphic (and print to console):

Graphics list#
The job of this component is to fetch all the graphics from Firebase and display them as a grid. The graphics can be fetched by dispatching the :a.d.f/fetch-graphics
event, but this is a side-effect!
The data should be fetched automatically when the component mounts. In modern React, this is achieved using the useEffect
hook. In traditional React, the component-did-mount
method is used to define the effects that need to be triggered when a component mounts.
To use the useEffect
hook, we need to reactify
Reagent components, ie convert them to React components. This means losing access to ratoms. Since Reframe's app-db
is just a Reagent atom under the hood, hooks don't play well with Reframe either.
It's a common approach in the Clojure frontend community to let the router handle mount interactions. This requires the developer to use a more bare-bones routing engine so the tweaks can be made.
But since we are using React Router, we'll use Reagent's create-class
method to create a class-based component. We'll then define a component-did-mount
method, and dispatch the ::fetch-graphics
event in that method:
Class-based components#
Unlike a functional Reagent component, a class-based Reagent component is defined by passing a map to reagent.core/create-class
method. This map accepts various key value pairs like:
:component-did-mount (fn [])
- equivalent to React'scomponentWillMount
:display-name
- name of the component for debugging and logging purposes:reagent-render
- equivalent to React'srender
A full list of accepted key-value pairs can be found in the official docs.
Graphics subscription#
We want our graphics-list
component to show a loading indicator when graphics are being fetched, then render each graphic. Let's create the subscription that pulls the required data from app-db
:
(rf/reg-sub
::graphics
#(select-keys % [::fetching-graphics? ::graphics]))
This will provide us with a map:
{:app.domain.firebase/fetching-graphics? false
:app.domain.firebase/graphics {:id1 {}
:id2 {}}
Namespaced destructuring of maps#
This page is a preview of Tinycanva: Clojure for React Developers