Reagent 101 / pt. 2 / Three Forms
Tags: clojure, react, tutorial
Published: 2020-07-07 02:40:14

In [Part 1](https://dev.to/icncsx/reagent-101-pt-1-hiccup-4k2c) 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.

```clojure
(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.

```clojure
(defn app-view []
  (let [counter (r/atom 0)] ;; setup
    (fn [] ;; render fn
      [:div
       [:p @counter]
       [:button
        {:on-click
         (fn [e]
           (swap! counter inc))}
        "Increment!"]])))
```

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](https://dev.to/icncsx/reagent-101-when-does-a-component-re-render-3461).

### with-let

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.

```clojure
(defn app-view [] ;; render function
  (with-let [counter (r/atom 0)] ;; execute once
    [:div
     [:p @counter]
     [:button
      {:on-click
       (fn [e]
         (swap! counter inc))}
      "Increment!"]]))
```

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.

```clojure
(defn app-view [arg]
  (let [state (r/atom {})]
    (reagent/create-class
      {:component-did-mount
       (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.

Warmly,
DH