re-frame

Nundrum 2023-12-13T16:36:50.435529Z

Here's a behavior question. I have a component with a text field. With :on-key-down for 13 it dispatches an event, which works. The text remains in the field. If click the field and hit enter again, the event dispatches but the value is blank. Alternatively if I try to reset! the ratom to blank it doesn't work. The text remains in the field. A println before and after shows the correct result in the console log. What am I missing?

p-himik 2023-12-13T16:43:16.519899Z

Can't answer without any code.

Nundrum 2023-12-13T16:46:34.594589Z

(defn heedy-string-object [obj]                                                       
 (let [text   (reagent/atom "")                                                     
       schema (get-in obj [:meta :schema])    ■ warning: unused binding schema     
       id     (:id obj)                                                             
       objname (:name obj)                                                           
       ]                                                                             
   ^{:key id} [:div.heedy-object                                                     
    [:p.heedy-object-description (or (:name obj) (:description obj))]                
    [:div.heedy-entry-line                                                           
      [:input.heedy-string {:size :14                                                
                            :type :text                                              
                            :name objname                                            
                            :on-change #(reset! text (-> % .-target .-value))        
                            :on-key-down                                             
                              #(case (.-which %)                                     
                                 13 (do                                              
                                      (println "before" @text)                       
                                      (re-frame/dispatch                             
                                        [::events/add-to-basket [id objname @text]])  
                                      (reset! text "")                               
                                      (println "after" @text)                        
                                      )                                              
                                 nil)                                                
                            }]

p-himik 2023-12-13T16:47:39.018839Z

Your ratom is overwritten on every render, making it useless. You have to use a form-2 or a form-3 component, or reagent/with-let.

Nundrum 2023-12-13T16:49:16.764199Z

If it's getting overwritten, then how does it work at all with the dispatch? How are the two printlns showing the results I expect - first the value of @text and then "".

p-himik 2023-12-13T16:51:39.873799Z

Ah, you don't dereference the ratom during the render. In any case, ratoms are not supposed to be used this way, so I wouldn't spend any amount of time on understanding what exactly is going on. It's better to introduce a proper fix and move on.

p-himik 2023-12-13T16:53:33.689939Z

If I were you, I'd make the input a controlled component, put the whole text into re-frame's app-db, move the input inside a form, and dispatch the add-to-basket event on a submission of that form while preventing the default action on the submit event.

Nundrum 2023-12-13T17:19:53.855569Z

I updated it to return a function, but then all my divs disappeared

(defn heedy-string-object [obj]    
 (let [text   (reagent/atom "")]  
   (fn []                         
     (let [id     (:id obj)      
           objname (:name obj) ]

p-himik 2023-12-13T17:23:51.480299Z

That's why some time ago, when you were asking about a way to learn about frontend development, I have suggested learning from the ground up and not in reverse. :) Can't say what's going on without seeing the full state of the current code that you have, but that inner (fn [] ...) should very likely be (fn [obj] ...).

p-himik 2023-12-13T17:24:19.476469Z

That's not always the case. Why and how to distinguish the cases is described in the excellent Reagent documentation.

Nundrum 2023-12-13T17:59:06.793879Z

If every cljs developer has to learn from the ground up by learning JS and friends first, cljs loses most of its luster.

p-himik 2023-12-13T18:05:17.356529Z

Not really. I never said that you have to learn the whole JS. Learning just the basics and the most frequently used API is enough and will go a long way while taking just a couple of days. Same deal with React and Reagent. You don't have to know everything in them by heart. But reading the most salient parts of the docs is IMO a must because it's the building blocks on which everything rests.

Nundrum 2023-12-13T18:07:27.834469Z

I have read through the Reagent docs

p-himik 2023-12-13T18:07:31.329359Z

Somewhat related - I remember teaching a junior Python programmer some pointer arithmetic from C and how memory works in general. It might surprise you but it ended up being quite useful for her because it let her have a much better grasp on the all the indexing patterns of Pandas dataframes. And it took me like half an hour to explain all that in a way that she understood.

Nundrum 2023-12-13T18:08:25.216509Z

But I still don't get why nothing renders. With obj or without obj on the inner function, it isn't getting called.

p-himik 2023-12-13T18:08:44.934119Z

Oh, so nothing renders? Or just that component?

liebs 2023-12-13T18:09:25.233939Z

well you would need to pass obj down into the anonymous function as a param, no?

p-himik 2023-12-13T18:10:05.837189Z

Most likely, yes. But Numdrum says that that didn't change anything.

👍🏻 1
Nundrum 2023-12-13T18:12:26.134369Z

Just that component. It behaves like the render function is never called.

Nundrum 2023-12-13T18:13:51.732329Z

obj isn't being updated - I just need the :name and :id from it and those always stay the same. So I think it's fine for it to be closed over? Only the ratom needs to be updated for the text field.

p-himik 2023-12-13T18:14:36.192079Z

If you turn a form-1 component into a form-2 component, it should render for the first time regardless of how you pass things around. If it's not rendered after the change, you have done something wrong.

Nundrum 2023-12-13T18:16:47.634479Z

Yeah, obviously so 😉 Any guesses as to what, though? Here's the full code. Where it was before I updated to form-2: https://github.com/robbieh/heedyfeedy/blob/dev/src/heedyfeedy/views.cljs#L35

p-himik 2023-12-13T18:17:35.944429Z

Well, if it's the change to form-2 that's wrong (given how that exact change made it stop render), how can I help if I don't have that new code?

Nundrum 2023-12-13T18:18:47.694889Z

That's the only change:

(defn heedy-string-object [obj]    
 (let [text   (reagent/atom "")]  
   (fn []                         
     (let [id     (:id obj)      
           objname (:name obj) ]

p-himik 2023-12-13T18:19:25.095009Z

That's not the only change because you also have the closing parens change. Please just post the whole component.

Nundrum 2023-12-13T18:24:56.468009Z

Hmmm I moved the inner let bindings to the outer since they never change and removed that inner let. And then they show up.

p-himik 2023-12-13T18:26:17.562889Z

Alright, I give up. Good luck though, and I'm happy you seem to have gotten it working.

Nundrum 2023-12-13T19:25:00.210059Z

Thanks! I'm glad it works. Long story short, it broke after reloading the page and worked again after changing (heedy-string-object) to [heedy-string-object] And now I'm glad I understand why!

p-himik 2023-12-13T19:31:46.986529Z

Right. And Reagent docs have a whole section describing () vs [] and why the latter should be used most of the time.

Kovas Palunas 2023-12-13T21:34:13.909569Z

Is there some documentation i'm missing about how to use form-3 reagent components with re-frame subscriptions? In my code at https://github.com/kovasap/cljs-board-game/blob/59ab8948727fa676c39bdf25a66d80956184962d/src/main/app/interface/view/developments.cljs#L123 I'm trying to get some data from a subscription (`developments`) into a :component-did-mount react lifecycle function. However, when I run the code, my data always resolves to nil. I've tried at multiple layers in the code with the same result, and am getting the sense that i'm misunderstanding something fundamental here

p-himik 2023-12-13T21:44:13.971879Z

The developments argument is not a subscription though. Its value does come from a subscription from a wrapping component, but it by itself is not a subscription. When you pass developments to the cytoscape-resource-flow component, the value gets closed over. The :component-did-mount will be called with that value, but nothing else will happen if the value changes on the outside. I don't know how you can get nil there though - sort-by always returns a collection, even if an empty one.

Kovas Palunas 2023-12-13T22:03:20.839069Z

i also tried putting the subscription directly into the :component-did-mount function, and got the same outcome

p-himik 2023-12-13T22:10:45.401669Z

Two ways around it. The first one - make the component non-reactive, accepting all the data from the outside. Its :component-did-mount function should do what it does, and you also must implement the :component-did-update function so it refreshes whatever the previous function has set up. The second one - similar, but deref all reactive things in the render function (no need to use the deref'ed values, you just gotta deref the reactions/ratoms so that Reagent registers them). And do the same thing with the lifecycle functions.

Kovas Palunas 2023-12-13T22:15:02.675459Z

hmm if i just copy the :component-did-mount into a :component-did-update key, i get the same outcome. i don't think this is exactly what you were recommending, but should do the same thing?

Kovas Palunas 2023-12-13T22:16:00.064089Z

your second suggestion seems to be the same as putting my reframe subscription/deref into the lifecycle function

Kovas Palunas 2023-12-13T22:16:04.530059Z

which also hasn't worked for me

p-himik 2023-12-13T22:18:29.536909Z

> i don't think this is exactly what you were recommending, but should do the same thing? Depends on the JS library that you're using, no idea. > your second suggestion seems to be the same as putting my reframe subscription/deref into the lifecycle function It's not the same. Are there any errors/warnings in the JS console in the browser?

Kovas Palunas 2023-12-13T22:18:53.184249Z

no unfortunately

Kovas Palunas 2023-12-13T22:19:31.961279Z

how does your second suggestion differ from

:component-did-mount (fn [_]
                              (cytoscape
                                (clj->js
                                  {:style     [{:selector "node"
                                                :style    {:background-color
                                                           "#666"
                                                           :label
                                                           "data(label)"}}
                                               {:selector "edge"
                                                :style    {:width 2
                                                           :line-color "#ccc"
                                                           :target-arrow-color
                                                           "#ccc"
                                                           :curve-style
                                                           "bezier"
                                                           :target-arrow-shape
                                                           "triangle"
                                                           :label
                                                           "data(label)"}}]
                                   :layout    {:name "circle"}
                                   :userZoomingEnabled false
                                   :userPanningEnabled false
                                   :boxSelectionEnabled false
                                   :container (js/document.getElementById
                                                graph-element-id)
                                   :elements  (make-development-graph
                                                @(rf/subscribe [:blueprints]))})))

p-himik 2023-12-13T22:20:31.079149Z

Using a reactive thing inside only a lifecycle method will not re-run that method on the reactive thing value change. I'm also not certain about whether lifecycle methods are considered a reactive context or not. If not, you'll get a warning from re-frame.

p-himik 2023-12-13T22:20:42.002489Z

Since you have a public repo, how do I reproduce this?

Kovas Palunas 2023-12-13T22:20:59.917939Z

you just clone, then ./run.zsh

Kovas Palunas 2023-12-13T22:21:17.492429Z

err

Kovas Palunas 2023-12-13T22:21:57.443509Z

have to install some stuff with npm as well

Kovas Palunas 2023-12-13T22:22:33.586419Z

ah do you mean that i may need to subscribe in the render method as well, even if i don't ever use the data there?

Kovas Palunas 2023-12-13T22:23:01.915959Z

just to trigger the re-calling of the lifecycle functions?

p-himik 2023-12-13T22:23:36.255769Z

The subscribe call can be on the inside of the wrapper function and outside of create-class. You just gotta deref the resulting reaction in the render function, yeah.

Kovas Palunas 2023-12-13T22:23:52.529129Z

ah let me try

p-himik 2023-12-13T22:25:33.152049Z

I got the app running. But how do I actually reproduce the behavior?

Kovas Palunas 2023-12-13T22:25:57.006609Z

you should see a graph rendered on the right of the page at localhost:3000

Kovas Palunas 2023-12-13T22:26:05.900609Z

btw your deref point seems to be working

p-himik 2023-12-13T22:26:18.461449Z

This?

Kovas Palunas 2023-12-13T22:26:18.722469Z

still messing with it but i see data now

Kovas Palunas 2023-12-13T22:26:21.507819Z

yes

Kovas Palunas 2023-12-13T22:26:33.901729Z

it should have the contents of developments in it too

Kovas Palunas 2023-12-13T22:26:43.057449Z

(more nodes and edges)

p-himik 2023-12-13T22:27:22.026139Z

> btw your deref point seems to be working Ah, alright. So I assume you got it from here. :) The first approach should've worked just as well. Must've been something wrong in the did-update method or something.

Kovas Palunas 2023-12-13T22:33:24.450959Z

yep all working. thanks!

👍 1
Kovas Palunas 2023-12-13T22:34:04.355489Z

this was the key

Kovas Palunas 2023-12-13T22:34:05.768909Z

it seems