What am I misunderstanding about unique keys in a seq here. This is my whole app so far, and I'm already getting warning
(defn day-row [day month year]
(let [key (str "row-" day "-" month "-" year)]
[:div
{:key key}
(str key)]))
(defn app []
(let [current-month 9
current-year 2024
days-in-month 30]
[:div
(for [day (range 1 (inc days-in-month))]
[day-row day current-month current-year])]))
(defn ^:dev/after-load start []
(dom/render [app]
(.getElementById js/document "app")))
(defn ^:export init []
(js/console.log "Initializing app")
(start))
(defonce initialize (rf/dispatch-sync [:initialize]))
Warning:
> template.cljs:247 Warning: Every element in a seq should have a unique 🔑 ([exfn.app.day_row 1 9 2024] [exfn.app.day_row 2 9 2024] [exfn.app.day_row 3 9 2024] [exfn.app.day_row 4 9 2024] [exfn.app.day_row 5 9 2024] [exfn.app.day_row 6 9 2024] [exfn.app.day_row 7 9 2024] [exfn.app.day_row 8 9 2024] [exfn.app.day_row 9 9 2024] [exfn.app.day_row 10 9 2024] [exfn.app.day_row 11 9 2024] [exfn.app.day_row 12 9 2024] [exfn.app.day_row 13 9 2024] [exfn.app.day_row 14 9 2024] [exfn.app.day_row 15 9 2024] [exfn.app.day_row 16 9 2024] [exfn.app.day_row 17 9 2024] [exfn.app.day_row 18 9 2024] [exfn.app.day_row 19 9 2024] [exfn.app.day_row 20 9 2024] [exfn.app.day_row 21 9 2024] [exfn.app.day_row 22 9 2024] [exfn.app.day_row 23 9 2024] [exfn.app.day_row 24 9 2024] [exfn.app.day_row 25 9 2024] [exfn.app.day_row 26 9 2024] [exfn.app.day_row 27 9 2024] [exfn.app.day_row 28 9 2024] [exfn.app.day_row 29 9 2024] [exfn.app.day_row 30 9 2024])
> (in exfn.app.app)
This displays on the screen:
Which all look unqiue to meKey has to be defined on the element which uses the component, it can't be set inside a component
So
^{:key key}
[day-row day current-month current-year]In your code :div element has a key, but it doesn't matter because the :div isn't the element in the seq
thank you, that makes sense and has fixed my issue! 👍
There is a nice pattern with into for this case, which obviates the need for :key in many cases.
Consider this implementation of app :
(defn app []
(let [current-month 9
current-year 2024
days-in-month 30]
(into [:div]
(for [day (range 1 (inc days-in-month))]
[day-row day current-month current-year]))))for dynamic lists, keys are important for performance and consistency reasons, you shouldn't get around them by using into.
for instance if you remove an element in the middle of the list, or reorder the list, React can avoid reconstructing the entire element tree if the keys are kept consistent. if you always return a "new" list of elements then it has to remount the entire list
I have never understood why into makes keys “unnecessary”. What are the conditions when react complains about missing keys, and how does the into trick solve that? And why can’t components include the key like in the example?
to answer the first question: when reagent converts your hiccup data to React elements, it converts all [:div [:span ,,,]] etc. into objects that React understands to represent DOM nodes.
"static" children like
[:div
[:span "foo"] [:span "bar"] [:span "baz]]
don't need keys because they don't change each render.
However, if you create a vector or seq that doesn't start with a keyword, then it treats it like a collection- which React assumes could change in size or be reordered
[:div
(for [word ["foo" "bar" "baz"]]
[:span word])
React will complaint that the above does not have keys associated with each element in the collection returned by for
Since the collection is actually static, it's trivial to rewrite it to the first snippet. However, if you wanted to "get around" needing keys for a dynamic collection, you could use into to construct hiccup data similar to the first snippet but not require keys
(into [:div] (for [word ["foo" "bar" "baz"]]
[:span word]))
;; => [:div [:span "foo"] [:span "bar"] [:span "baz"]]the second question is more complex to succinctly explain the "why?" in a slack message but suffice to say that keys must be associated with the elements in a collection without having to render the components associated with the elements
Thanks @lilactown!