Skip to content

Step 02: Presenting data with components

Valentin Waeselynck edited this page Mar 3, 2018 · 9 revisions

Browse code - Diff - Live demo

To reset your workspace for this step, run git checkout step-2.


Now that we know how to create views in Reagent, we'll generate these views dynamically from data.

Adding our model data

First, we add a list of phones to our namespace; for the sake of brevity, it's hardcoded for now, but we could imagine fetching it from a Web Service, or a database, etc.

(def hardcoded-phones-data [{:name "Nexus S" 
                             :description "Fast just got faster with Nexus S"}
                            {:name "Motorola XOOM™ with Wi-Fi" 
                             :description "The Next, Next Generation tablet."}])

Defining our view with components

Now, we define components to present this data in our view. In Reagent, a component is usually defined as a simple ClojureScript function, which takes some data as arguments and returns a data structure representing the DOM, just like what we saw in the previous step.

(declare ;; here we declare our components to define they're in an order that feels natural.  
  <phones-list>
    <phone-item>)

(defn <phones-list> "An unordered list of phones"
  [phones-list]
  [:div.container-fluid
   [:ul
    (for [phone phones-list]
      [<phone-item> phone]
      )]])

(defn <phone-item> "An phone item component"
  [{:keys [name description] :as phone}]
  [:li.phone-item 
   [:span name]
   [:p description]])

(defn mount-root "Creates the application view and injects ('mounts') it into the root element." 
  []
  (rg/render 
    [<phones-list> hardcoded-phones-data]
    (.getElementById js/document "app")))

This is a very intuitive way of refactoring our previous view definition in a way that is more dynamic and has less repetitions.

Note: for naming Reagent components, I just used the <component-name> naming convention, to be reminiscent of HTML tags. This is just a naming convention, and of course it will not be interpreted in any special way by Reagent.

However, when writing our components functions, we don't invoke them as we usually would. For instance, in the <phones-list> component, instead of writing (<phone-item> phone), which would be function invocation, we just put the function and its arguments in a vector: [<phone-item> phone], which is processed in a special way by Reagent.

This is how we make Reagent aware of our components hierarchy, which enables it to convert our components into React components.

Final refinements

If you load the page with the previous code, the page will display all right, but you will get 2 warnings in the browser console:

Warning: Every element in a seq should have a unique :key (in reagent_phonecat$core$phones_list). Value: [...]

and:

Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method of    reagent_phonecat$core$phones_list. See https://fb.me/react-warning-keys for more information.

The second warning is a low-level React warning about lists of children components, and the first warning is Reagent telling you how to avoid the second warning.

This is a general problem you encounter with lists of components in React: basically, React is warning you that if you don't provide a way of uniquely identifying each item in the list, it may have difficulties keeping track of the items when the list changes. You can read more about this in React documentation about dynamic children. The solution to this problem is to attach a key attribute to each item; in Reagent: we can do it by defining a :key value in the metadata map of the item:

(defn <phones-list> "An unordered list of phones" 
  [phones-list]
  [:ul 
   (for [phone phones-list]
     ^{:key (:name phone)} [<phone-item> phone]
     )])

Summary

  • Very intuitively, a component in Reagent is a function that accepts data and returns a data structure representing the DOM; we call this a rendering function.
  • In this data structure, other components may be called
  • Under the hood, Reagent will translate these data structures into React's virtual DOM
  • When you have a sequence of components inside a rendering function, don't forget to add :key metadata.

In the next step, we'll learn to handle user input and state.

Clone this wiki locally