Dynamic children in a Reagent component


@mccraigmccraig asked on clojurian’s slack why the following Reagent component causes a React warning:

(defn an-integer
  [n]
  ^{:key n}
  [:li n])

(defn calling-component []
  [:div "Parent component"
   (for [n (range 0 10)]
     [an-integer n])])

whereas a similar looking component doesn’t:

(defn an-integer
  [n]
  [:li n])

(defn calling-component []
  [:div "Parent component"
   (for [n (range 0 10)]
     ^{:key n}
     [an-integer n])])

Initially my reaction was that maybe its Reagent which is causing this issue but a closer look at the documentation for this topic clarifies this exact scenario.

The key should always be supplied directly to the components in the array, not to the container HTML child of each component in the array:

So in the Clojurescript context, the second code above is the correct way to do it.

And I think this makes sense. In the first example, an-integer is returning the key in what becomes React’s render method whereas you want to get the key while you are creating the virtual dom without having to do call render as in the second example.

Aside, the following two Reagent components are exactly the same thing:

(defn calling-component-1 []
  [:div "Parent component"
   (for [n (range 0 10)]
     ^{:key n}
     [:li n])])
(defn calling-component-2 []
  [:div "Parent component"
   (for [n (range 0 10)]
     [:li {:key n} n])])

Reagent when creating the React component checks if the metadata has a key called key and if so it adds it to the components props. More here.

Mike Thompson has a great answer on Stack Overflow about the need for a key and different strategies to create children with/without keys.