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
[: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.
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.
(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.
(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
Top comments (0)