Cover image for Reagent 101 / pt. 2 / Three Forms

Reagent 101 / pt. 2 / Three Forms

icncsx profile image DH Kim Updated on ・3 min read

In Part 1 of the Reagent series, we talked about the Hiccup syntax to describe UI. Now we are equipped to talk about the 3 types of Reagent components.

Form 1 / function that returns Hiccup

This is the most common form you'll see. A Reagent component can just be a function that returns a Hiccup vector (the JSX of Reagent) or another component.

(defn app-view [] ;; render fn
  [:h1 "Hello, world"])

A Form 1 Component, then, is simply a render function. It returns some stateless UI.

Note: A render function is a mandatory part of any component in Reagent just as it is the case in React.

In all 3 forms, a render function is provided. Form 1 is the simplest - it's just the render function. The other two forms differ only in terms of what they offer in addition to the render function.

Form 2 / function that returns a render function

This form is used when you want to do some initialization (e.g. initialize some local state) when the component is first created.

(defn app-view []
  (let [counter (r/atom 0)] ;; setup
    (fn [] ;; render fn
       [:p @counter]
         (fn [e]
           (swap! counter inc))}

You can think of a Form 2 component as a function that returns a Form 1 component. Emphasis on return - meaning we can do whatever we want before the return.

In React, we typically initialize some local state inside the constructor(). In Reagent, we can do something similar inside a Form 2 component before returning the render function. In this example, we are initializing the counter atom that keeps track of how many times we click on a button.

The outer function (app-view) will be called once per component instance and create the state for that instance, whereas the inner function (the anonymous render function) will be called at least 1 time. We say at least because a component may not re-render if it doesn't have to. More on this in another post.


The with-let macro is syntactic sugar that makes your Form 2 component look like a Form 1 component. This is because with-let bindings only execute once.

(defn app-view [] ;; render function
  (with-let [counter (r/atom 0)] ;; execute once
     [:p @counter]
       (fn [e]
         (swap! counter inc))}

Some people prefer the with-let style of defining a Form 2 component while others prefer to wrap the inner render function with a let. It's up to you!

Form 3 / reagent/create-class

React as you know has full life-cycle methods for its components. Here are a few I can think of off the top of my head.

  • componentDidMount
  • componentWillUnmount()
  • shouldComponentUpdate()
  • ...

Because Reagent is an interface for React, we can also access React's full life-cycle methods.

A Form 3 component is a function that returns a JavaScript class via reagent/create-class. And because we are using React.createClass under the hood, the interface of a Form 3 component will look similar to that of a class-based React component.

(defn app-view [arg]
  (let [state (r/atom {})]
       (fn [comp] (println "I mounted"))

       ;; more life-cycle methods

       :reagent-render ;; render fn
       (fn [arg]
         [:h1 "Hello, world"])})))
  • component-did-mount is the React life-cycle method. It is going to be called right after the component is mounted to the DOM.

  • reagent-render is the render method. Here, we have an explicit render. In Form 1 and Form 2, this was made implicit for us - either as the inner function or the entire function (Form 1).

Now that you know about the 3 types of Reagent components, you're in good form to move on to the next post.


Posted on by:

icncsx profile

DH Kim


Software engineer interested in cloud computing and serverless backend services.


markdown guide