Fork me on GitHub
#re-frame
<
2020-09-30
>
Gesprye20:09:51

Hello everyone! I'm learning re-frame, and I'm stuck with a problem I don't understand. I started from a re-frame lein template and I tried to add a list with items having keys this way: [:li {:key i :whatever 3} (str "Item #" i)] . But the :key disappears and is not reproduced in the generated HTML, and React complains that the items are missing a key property. Strangely, whatever="3" is correctly added as an attribute. Can someone explain to me what I'm doing wrong here? Many thanks for your help!

p-himik20:09:00

The :key is not supposed to end up in DOM. Are you sure i is not nil and is unique across all siblings? Are you sure React gives you warnings about these particular :li?

kennytilton20:09:05

Here is an example from my code:

[:ul {:style (merge utl/hz-flex-wrap
               {:list-style "none"
                :padding    0
                :margin     0})}
 (map (fn [jsort]
        (let [{:keys [title]} jsort]
          ^{:key title} ;;; <----- BINGO!!!!!!!!!!!!!!!!!!!!!!!
          [:li [sort-bar-option jsort]]))
   utl/job-sorts)]

p-himik20:09:31

It can be in the metadata or it can be in the props map - either should work.

👍 3
p-himik20:09:44

In the above example, I would just use into, maybe with :<>.

kennytilton20:09:13

Actually, I just noticed “the `:key` disappears and is not reproduced in the generated HTML”. I would not necessarily expect the React-specific “key” to propagate thru to the HTML.

p-himik20:09:39

That was my first statement. :)

🍺 3
Gesprye20:09:50

Thanks for this feedback! However, React is indeed complaining about a missing key attribute, and I don't know how to provide it (I tried both directly in the map and through a ^{:key i} metadata...

Gesprye20:09:58

Here is my code:

Gesprye20:09:24

(defn main-panel [] (let [name (re-frame/subscribe [::subs/name])] [:div [:h1 "Hello from " @name] [:ul (for [i (range 4)] [:li {:key i :whatever 3} (str "List item #" i)])]]))

p-himik20:09:31

I don't know why it doesn't work. But you can just replace the :ul block with

(into [:ul]
  (map (fn [i]
         [:li {:whatever 3} (str "List item #" i)])
  (range 4))

Gesprye21:09:30

OK, so the key should not appear as an attribute in the DOM then? It is just used by React internally?

Gesprye21:09:48

Great, thanks for this! I'll check it on my more involved example (with :td in a table).

superstructor22:09:59

into as per @U2FRKM4TW's example is a good solution, based on your earlier code this also works for me:

(defn main-panel []
  (let [name (re-frame/subscribe [::subs/name])]
    [:div
     [:h1 "Hello from " @name]
     [:ul
      (for [i (range 4)]
        ^{:key (gensym)}
        [:li {:whatever 3} (str "List item #" i)])]]))

p-himik06:10:44

On no, don't use gensym. It will make the items re-render each time the parent component is touched.

💯 3
p-himik06:10:41

Don't. Use. Synthetic. Keys. Avoid using laziness and 95% of your key problems will go away.

p-himik06:10:09

Laziness in rendering does not work and cannot help.

kennytilton20:09:46

I think we need a meta-data trick here. brb…