Fork me on GitHub
#reagent
<
2020-10-27
>
clumsyjedi01:10:09

Good morning I am trying to ue a small wrapper function in reagent/re-frame to generate bootstrap/hiccup vecs. An example for a bootstrap column would be:

(defn col [{:keys [md]}. & children]
  [:div {:class (class-name (if md (str "col-md-" md) "col"))}
    children])
Used like
[col {} [:div "hi"]]
What happens in this example is that [:div "hi"] is captured by the [& children] in the params vector of col and converted to [[:div "hi"]] which is well and good, but reagent complains about this
Warning: Every element in a seq should have a unique :key
So, ok, no worries, I can avoid the error by calling col as
[col {} ^{:key "hi"} [:div "hi"]]
And the error goes away. I'm not crazy about this however because I have to pepper these metadata declarations around my code, even when a col has only one child. So my question is: Can col be rewritten to set the metadata on the children , avoiding the need to do so in the calling code. I haven't been able to get a working example.

phronmophobic01:10:43

two options come to mind: 1. use into. I think this will get rid of the warnings when there's one child

(defn col [{:keys [md]} & children]
  (into [:div {:class (class-name (if md (str "col-md-" md) "col"))}]
        children))
2. provide some way to automatically derive a key. This example just uses the index, but you derive a key from some part of the children passed in
(defn col [{:keys [md]} & children]
  [:div {:class (class-name (if md (str "col-md-" md) "col"))}
        (map #(with-meta %1 {:key %2}) children (range))])

💯 6
clumsyjedi02:10:26

great response, thanks @U7RJTCH6J

Александр Стоянов03:10:26

Hello! I'm learning clojurescript now and have some problem: (def click-count (atom 0)) (defn b [] [:div "The atom " [:code "click-count"] " has value: " @click-count ". " [:input {:type "button" :value "Click me!" :on-click #(swap! click-count inc)}]]) in `main-panel`: (b) So that's almost work. I have button on my page but it doesn't refresh. First value is 0 and when i click on button value don't changes. It changes only when i clicked some times then re-build it and it shows quantity of clicks. So what i should do to refresh it without re-building?

Lucy Wang06:10:09

1. you should use reagent.core/atom instead of clojure.core/atom 2. use [b] instead of (b)

☝️ 3
Lu10:10:24

@mister.stoyanov13 To get data from a text input you want to use the :on-change handler. The event will be passed as a parameter in the callback function. Remember to use js interop when dealing with the event object, as it is a JS object in fact. A basic example is this:

(def state (r/atom {:input-value ""}))

(defn foo []
  [:input
   {:value (:input-value @state)
    :on-change (fn [js-event]
                 (swap! state assoc :input-value (-> js-event .-target .-value)))}])

Lu10:10:38

Notice that if you want to get any information about the button input i.e. its value, you can similarly use (-> js-event .-target .-value) in its on-click callback

Christopher Stone13:10:54

Hi Clojurians, I am relatively new to using reagent and have reached a bit of a stumbling block, I have searched for an answer for a few months now, trying to find a code example but keep being led back to the reagent docs that do not have an answer to my problem... I am trying to display a map using leaflet using adapt-react-class and have made some components that work with one another to display a map with various markers, collections of markers and geometry... the problem I am having is that I have no idea how to access methods on the adapted react class to add things to the map after the initial creation of the map, specifically using the Map.addTo() method... how do I access methods on an adapt(ed)-react-class, any help would be greatly appreciated

Lu14:10:27

You can store your instance in an atom and refer to it when you want to call methods.. to give you an example:

(defn my-leaflet
  []
  (let [leaflet-state (atom nil)]
    (r/create-class
     {:component-did-mount
      (fn [props]
        (reset! leaflet-state (.setView (js/L.map (r/dom-node props)
                                                  #js {:zoomControl false})
                                        [50 50] 3)))
      :reagent-render
      (fn []
        [:div.leaflet-picture])})))

Christopher Stone15:10:30

Thanks Lu for replying so quickly, just a couple of questions, In the code snippet you sent me I'm assuming that its a r/atom that I would use, is that right? can you please clarify... I am using react-leaflet, lifted from https://github.com/metosin/komponentit/blob/master/example-src/cljs/example/map.cljs. I am also using shadow-cljs and am not sure if I need to place the the react-leaflet dependancy string in the shadow-cljs.edn file or if its okay to just use "yarn add" (the latter is the method we used)... at the start of the file you will see that he setup a few global defs that break up the component parts and use them later in the code in one larger component... how could I achieve what I asked earlier in this situation? Thanks again

valtteri15:10:01

React-leaflet is a declarative wrapper around imperative leaflet api. You don’t (usually) call any leaflet functions to make changes. Instead you create react components that describe what elements, content and functionality the map should have. Check react-leaflet docs for examples https://react-leaflet.js.org/docs/en/intro.html

valtteri15:10:05

And yes, you can install react-leaflet from npm