Fork me on GitHub
#reagent
<
2017-01-16
>
Oliver George00:01:12

I think my workaround will be "callback updates state to request page of results be loaded"

timothypratley06:01:28

Can someone help me out with a question that I'm not sure how to ask... Sometimes using the metadata :key works well eg:

(defn list-by [entities sort-k]
  [:ul
   (for [[k v] (sort-by (comp sort-k val) @entities)]
     ^{:key k}
     [:li (:name v)])])
But sometimes it doesn't, eg:
(def words
  ["ice" "cream" "chocolate" "pastry" "pudding" "raspberry" "mousse"
   "vanilla" "wafer" "waffle" "cake" "torte" "gateau" "pie" "cookie"
   "cupcake" "mini" "hot" "caramel" "meringue" "lemon" "marzipan" "mocha"
   "strawberry" "tart" "custard" "fruit" "baklava" "jelly" "banana" "coconut"])

(defn rand-name []
  (string/capitalize (string/join " " (take (+ 2 (rand-int 5)) (shuffle words)))))

(def desserts (reagent/atom ()))

(defn make-a-dessert [e]
  (swap! desserts conj {:id (random-uuid)
                        :name (rand-name)}))

(defn make-many-desserts [e]
  (dotimes [i 100]
    (make-a-dessert nil)))

(defn color-for [x]
  (str "#" (.toString (bit-and (hash x) 0xFFFFFF) 16)))

(defn dessertinator []
  [:div
   [:button {:on-click make-a-dessert} "Invent a new dessert"]
   [:button {:on-click make-many-desserts} "Invent 100 new desserts"]
   [:ol
    (for [{:keys [id name]} @desserts]
      ^{:key (doto id (js/console.log))}
      [:li
       [:svg {:width 50 :height 50}
        [:circle
         {:r 20 :cx 25 :cy 25 :fill (color-for id)}]
        [:rect {:x 15 :y 15 :width 20 :height 20 :fill (color-for name)}]]
       [:span [:em [:strong name]]]])]])

timothypratley06:01:42

putting it as a {:key id} attribute works fine

timothypratley06:01:01

But I don't really understand the rules for when the metadata form works and does not work.

timothypratley06:01:55

(defn f []
  [:ol
    (for [d @desserts
                 :let [_ (js/console.log (:id d))]]
      ^{:key (:id d)}
      [dessert d])])

(defn dessertinator []
  [:div
   [:button {:on-click make-a-dessert} "Invent a new dessert"]
   [:button {:on-click make-many-desserts} "Invent 100 new desserts"]
   [f]])
does work...

timothypratley06:01:11

so it seems to be related to the structure of the component

timothypratley06:01:03

i.e.: if metadata is attached deeper than level 2 it doesn't seem to "work" (sorry for the hand wavy language)

gadfly36106:01:34

So in your last example, if you wrap fs ol hiccup in a button (instead of doing it in desertinator) it stops working?

timothypratley06:01:28

hmmm great question. I just tried wrapping it in a :div, and it didn't produce a warning, so my theory is clearly wrong 🙂

timothypratley06:01:40

oh I bet I know what the issue is....

timothypratley06:01:55

its a klipse specific thing nevermind lol

gadfly36106:01:55

Wouldnt think it would matter, but in your failing example, if you move the console.log from the metatdat and place it in the for let block .. does that change anything?

timothypratley06:01:24

So klipse reagent code has a slight detail where the LAST form is a little special

timothypratley06:01:44

if I just define my dessertinator as the 2nd last form, it's fine.

timothypratley06:01:46

Thanks for the sanity check!!! 🙂

minikomi10:01:09

(defn transition-test [component]
  [TransitionMotion
   {:styles (if component
              [{:key "panel"
                :default-style {:x 0}
                :style {:x (spring 100)}}]
              [])
    :will-enter (fn []
                  #js {:x  0})
    :will-leave (fn []
                  #js{:x (spring 0)})}
   (fn [x]
     (reagent/create-element
      (reagent/reactify-component
       (fn [anim-obj]
         (println anim-obj)
         (when-not (empty? (anim-obj :children))
           (let [x (-> anim-obj
                       :children
                       first
                       (gob/get "style")
                       (gob/get "x"))]
             [:div#panel
              {:style
               {:transform (str "translate(" (- 100 x) "px, 0)")
                :opacity (/ x 100)}}
              (with-out-str (prn anim-obj))
             component])
            )))
      #js{} x))])
It’s alive

minikomi10:01:30

(defn Panel-child [{:keys [styles children]}]
  (when styles
    (let [x (.. styles -style -x)]
      [:div#panel
       {:style
        {:transform (str "translate(" (- 100 x) "px, 0)")
         :opacity (/ x 100)}}
       children])))

(defn animated-panel [children]
  [TransitionMotion
   {:styles (if children
              [{:key "panel"
                :default-style {:x 0}
                :style {:x (spring 100
                                   {:stiffness 320 :damping 30})}}]
              [])
    :will-enter (fn []
                  #js {:x  0})
    :will-leave (fn []
                  #js{:x (spring 0 {:stiffness 320 :damping 20})})}
   (fn [styles]
     (reagent/create-element
      (reagent/reactify-component Panel-child)
      #js{:styles (aget styles 0)} children))])
a little better..

rovanion14:01:29

Can someone explain why I can't use state defined in a let with reagent? http://paste.debian.net/908989/

rovanion14:01:47

The only difference is the scope of the state and the contents of the let shouldn't be GC'd because it's still referenced isn't it.

mccraigmccraig14:01:06

@rovanion what the surrounding context - state defined in a let works fine with reagent, so something else must be awry

rovanion14:01:39

The surrounding context is a div. This is all:

(def chat-state (atom ""))
(defn chat []
  [:div {:id :chat}
   (let [chat-state (atom "")]
     [:textarea   
      {:value     @chat-state
       :on-change #(reset! chat-state (-> % .-target .-value))}])
   [:textarea
    {:value     @chat-state
     :on-change #(reset! chat-state (-> % .-target .-value))}]])
This is then rendered with reagent/render.

mccraigmccraig14:01:54

@rovanion the problem is you have a form-1 component, but you either want to use a form-2 component or def the state outside the render fn (which you have discovered)... see https://github.com/Day8/re-frame/wiki/Creating%20Reagent%20Components for a discussion of the different component types

rovanion15:01:55

Had no idea there were different forms of components.

rovanion15:01:25

Thank you so much @mccraigmccraig! Returning an inner rendering function made it so that the atom isn't redefined every time the component is rendered, so that it's only defined once at the beginning so to speak.

rovanion15:01:56

But that was all just so that I could write this other SO question: http://stackoverflow.com/questions/41680143/how-is-a-chat-input-field-defined-in-reagent It's about trying to implement a chat input text-area like that here on slack, where pressing the enter key sends the message.

mccraigmccraig15:01:52

it's a stupid simple solution to overflow, but it works ok

pesterhazy18:01:05

ANN: Recently I've been working on a Reagent Toolbox library called recalcitrant: https://github.com/pesterhazy/recalcitrant. It's still early stages. But the basic idea is to make lifecycle methods composable: start with a render fn, add some side-effect, add logging, add resource management for a timer

pesterhazy18:01:09

Generally recalcitrant should help with the somewhat onerous task of building stateful components. I'd be curious if people think that could be useful

pesterhazy18:01:18

There's also a component that does Error Handling in interactive development in there, it's proven helpful when working on SPAs.

timothypratley22:01:55

@pesterhazy looks very helpful! nice idea/tool