re-frame

https://github.com/Day8/re-frame/blob/master/docs/README.md https://github.com/Day8/re-frame/blob/master/docs/External-Resources.md
Oz 2020-06-30T08:43:52.149600Z

Hey, I'm wondering if my code is an anti-pattern I have a table in my app database that get populated with up to hundreds of keys representing some data tags. I need to be able to subscribe to these tags but manually writing an individual function for every tag didn't make sense as all of them would've been identical besides the tag name, so I wrote a function to register the subs for all the tags.

(defn register-subscription-for-tag
  "make a sub for a tag"
  [tag]
  (re-frame/reg-sub
   (keyword tag)
   (fn [db _]
     ((keyword tag) (:tag-table db)))))
But this still creates hundreds of subs, that are only for a simple get and it feels like an overkill, I'm suspecting this is part of the reason why tools like re-frame10x and re-frisk get into some performance issues*. I was thinking of using dynamic subscriptions, but I'm not sure if its not deprecated and going to be removed soon. Do you think the current function is legit or is there a better solution? *well another suspect is too many events dispatched to send and recieve websocket data, but maybe it is the hundreds of subs.

p-himik 2020-06-30T08:55:29.149700Z

I'd say it's not an antipattern if you create all the subs during your app initialization and not during its regular operation. AFAIK the dynamic subs have been deprecated because there's no need for them - you can chain subs directly:

(let [x @(subscribe [:x]), y @(subscribe [:y x])] ...)

Setzer22 2020-06-30T09:33:16.151200Z

I'm interfacing with a react component (Reactour https://github.com/elrumordelaluz/reactour), that expects some props with arbitrary html content. And I don't know how to pass them in clojurescript. In React, they'd create the component as:

<Tour
  ...
  steps=[{
    selector: '[data-tut="reactour__iso"]',
    content: `Ok, let's start with the name of the Tour that is about to begin.`
  }]/>

Setzer22 2020-06-30T09:34:21.152200Z

In clojurescript, I do this, and it works fine:

[:> Tour 
  {...
   :steps [{:selector "...", :content "..."}]}]

Setzer22 2020-06-30T09:35:11.153Z

however, the library also allows passing in DOM elements as part of the content, like so:

<Tour
  ...
  steps=[{
    selector: '[data-tut="reactour__iso"]',
    content: <button>Click me!</button>,
  }]/>

Setzer22 2020-06-30T09:35:52.153900Z

but if I pass the equivalent hiccup map [:button "Click me!"] in clojurescript, that gets coerced to a string and rendered as text. How can I make it so whatever I pass in as content gets converted to DOM elements?

p-himik 2020-06-30T09:39:02.154400Z

Use reagent.core/as-element.

Setzer22 2020-06-30T09:39:50.154600Z

thanks! that worked πŸŽ‰

Oz 2020-06-30T09:57:18.154800Z

Thank you! Well, the code works fine, but I needed a sanity check πŸ™‚ It looks like the debug tools are bothered by the large amount of events, so I'll try configure them to ignore some of them.

Phil Hunt 2020-06-30T10:42:27.158200Z

Has anything changed in the last year or so that would cause the following code to not return a function that returns the expected values? (re-frame/reg-sub :cell (fn [db [_ x y]] (reaction (get-in @db [:board [x y]])))) Where :board in db is demonstrably something like :board {[2 0] :x, [2 1] :o, [2 2] :x, [1 2] :o, [1 1] :x}

2020-06-30T10:43:22.158600Z

That would never have worked.

Phil Hunt 2020-06-30T10:43:32.159Z

Ahh

2020-06-30T10:43:39.159200Z

Perhaps if you substitute reg-sub for reg-sub-raw ?

Phil Hunt 2020-06-30T10:44:13.159700Z

OK, it's register-sub in the code I'm trying to re-engineer

Phil Hunt 2020-06-30T10:45:06.160300Z

Fabulous πŸ™‚

Phil Hunt 2020-06-30T10:45:13.160500Z

Thanks so much Mike

Phil Hunt 2020-06-30T10:46:22.161700Z

So register-sub is deprecated for reg-sub and reg-sub-raw?

2020-06-30T10:49:38.162500Z

It will still work, but you'll get a depricated warning

2020-06-30T10:50:01.163100Z

You should just rename to reg-sub-raw

Phil Hunt 2020-06-30T10:50:55.164300Z

didn't work in my context, but -raw does. I suspect the stuff I'm following isn't quite idomatic Re-frame though based on the 'you should probably be using reg-sub' note in the docs.

2020-06-30T10:50:56.164400Z

or rewrite slightly and use reg-sub

2020-06-30T10:52:53.165600Z

(re-frame/reg-sub                   ;; change name 
 :cell
 (fn [db [_ x y]]                   ;; get rid of the `reaction` code
   (get-in db [:board [x y]]))))    ;; remove @ in front of db

Oz 2020-07-01T07:50:04.185400Z

What I was looking for, but not what I needed

Phil Hunt 2020-06-30T10:53:47.166300Z

Ooh OK. That's much more understandable

Phil Hunt 2020-06-30T10:54:15.166900Z

thanks. I was getting hung up on trying to understand why it needed an explicit call to reaction

Phil Hunt 2020-06-30T10:56:14.167200Z

Thanks for the help !

Oz 2020-06-30T12:12:14.167300Z

Hey, that's what I was looking for πŸ™‚

2020-06-30T13:40:51.176400Z

Hi, I am rewritting subscriptions within our startup (http://orgpad.com) and I have some general questions. I started with very simple subscriptions which were too complicated and recomputed frequently. The main model is a graph where I have two maps, one from UUIDs to nodes and the other from UUIDs to links, and each value is a further map containing multiple properties. I used to have a single subscription for each node and each link which was recomputed whenever any property changed (label, color, position, even when it was dragged by a mouse). Currently, I switched into multiple subscriptions, for individual properties. For instance, when I press a mouse button on anything, there is one subscription for that, used by another subscription saying that the press occured on a node, and then a subscription for each node saying that the press occured on this particular node. And only the last subscription influences the node position, but nothing unrelated in the map. Less should be recomputed right now, but the exact tradeoff is not clear to me. 1. What is the extra cost of running a single small subscription? 2. How much slower is to use @(subscribe ...) within a subscription? I am using this sometimes when parameters of a subscription depend on some value extracted by other subscriptions. For instance, a link contains a pair node ids which it connects, so if I want to now its start and end position, I will first obtain these ids and then dynamically subscribe to their positions. 3. Is there a good way to analyze performance of subsciptions?

2020-07-01T07:58:30.185600Z

I thought that that you was using subscribe in the main body of reg-sub, which is not the way it was intended to be. As for Layer 3 subsription performance I think it depends. According to @mikethompson

And, to answer you question, layer 3 are not about less rendering (in general).  The propagation pruning layer is Layer 2.  Layer 3 is about performing the the expensive computation.

2020-07-01T10:10:48.190500Z

So if you can't use subscribe in the main body of reg-sub, how can you achieve what I described in 2?

2020-07-01T10:41:30.192200Z

But what if I need to subscribe to a subscription, whose parameter depends on some other value I obtain from another subscription.

2020-07-01T10:42:00.192400Z

Say, in my case, a link has two endpoints and only need to depend on their positions and not on position of all units.

2020-07-01T10:42:41.192600Z

I could in principle obtain the ids of endpoints in my view and send them to another subscription, but that seems incorrect to me since subscription logic will be leaking into my views.

2020-07-01T10:43:50.192800Z

Using @(rf/subscribe [:node/pos endpoint-id]) works within the main body of my subscription. So my question is what is the downside of using it like this?

p-himik 2020-07-01T12:09:50.193100Z

No downsides. As to your question about feeding sub value into another sub, you can just use reg-sub-raw for that.

p-himik 2020-07-01T12:10:23.193300Z

Ah, sorry, I missed the fact that you deref inside the body of a subscription and not a view. Here, I'm not sure.

2020-07-01T13:56:33.193600Z

So basically the correct way would be to use reg-sub-raw. I will take a look at it how it works precisely.

2020-07-01T19:13:42.211200Z

> Using @(rf/subscribe [:node/pos endpoint-id]) works within the main body of my subscription. So my question is what is the downside of using it like this? There's a possibility of memory leak because derefs are tracked and it is possible that subscription won't be disposed.

2020-07-01T20:37:58.225300Z

Ok, so I read this http://day8.github.io/re-frame/flow-mechanics/ and using reg-sub-raw instead makes sense for my case. Btw. I am missing a real example which would need reg-sub-raw in the text. The example here is better: https://github.com/day8/re-frame/wiki/Dynamic-Subscriptions

2020-07-02T07:49:54.244Z

I think reg-sub-raw is more advanced, so it is not in the main docs. But better to ask @mikethompson

2020-06-30T18:05:44.176600Z

Looks like for p 2. you probably should read about Layer 3 subscriptions https://github.com/day8/re-frame/blob/master/docs/subscriptions.md#reg-sub

knubie 2020-06-30T18:34:17.177200Z

quick question: when looking at traces in re-frame10x, what is the :raf operation?

2020-06-30T19:12:05.177300Z

I am familiar with this level of description, but I would like to get a more advanced insight.

ruben.hamers 2020-06-30T19:40:28.181700Z

Hey guys I'm writing a reagent/re-frame frontend for a reitit (example) backend: https://github.com/metosin/reitit/tree/master/examples/ring-spec-swagger I'm currently writing the frontend to upload a file to the backend but I cannot get the upload Ajax request setup properly. I have created a form with `enc-type multipart/form-data' and send the formdata object via a re-frame event handler. I think the js/formdata is not set correctly. The response has error-code 400 with data:

{"spec":"(spec-tools.core/spec {:spec (clojure.spec.alpha/keys :req-un [:spec$10934/file]), :type :map, :leaf? false})","problems":[{"path":[],"pred":"(clojure.core/fn [%] (clojure.core/contains? % :file))","val":{},"via":[],"in":[]}],"type":"reitit.coercion/request-coercion","coercion":"spec","value":{},"in":["request","multipart-params"]}
Upload form:
(defn upload-component []
  [:div
   [:form {:id       "upload-form"
           :enc-type "multipart/form-data"
           :method   "POST"}
    [:label "Upload Filename: "]
    [:input {:type "file"
             :name "upload-file"
             :id   "upload-file"}]]])
Function Invoked when a button is pressed:
(defn prepare-form-data [element-id]
  (let [el (.getElementById js/document element-id)
        name (.-name el)
        file (aget (.-files el) 0)
        form-data (doto
                    (js/FormData.)
                    (.append name file))]
    (println el)
    (println name)
    (println file)
    (println form-data)

    (rf/dispatch [:request-files-upload {:file form-data}])
    ))
Event handler:
(rf/reg-event-fx
  :request-files-upload
  (fn-traced [{:keys [db event]} [_ a]]
             (let [params (second event)
                   url (str (:backend-url environment) "/files/upload")]
               {:http-xhrio {:method          :post
                             :uri             url
                             :timeout         8000
                             :format          (ajax/json-request-format)
                             :response-format (ajax/json-response-format {:keywords? true})
                             :params          params
                             :on-success      [::good-files-upload-result]
                             :on-failure      [::bad-files-upload-result]}})))

ruben.hamers 2020-07-02T04:32:06.241300Z

So this is how the request looks like in insomnia butI cant get it working in cljs through cljs-ajax :

(client/post "<http://127.0.0.1:3000/files/upload>" {:multipart [{:name "file"
                                                                :content "ajsdnasd
                                                                    "}]})

p-himik 2020-07-02T06:00:53.241600Z

Try not specifying :format at all and instead of :params pass an instance of https://developer.mozilla.org/en-US/docs/Web/API/FormData as :body.

p-himik 2020-07-02T06:01:07.241900Z

That's how I do it.

Ruben.Hamers 2020-07-02T06:42:43.243600Z

ok, ill give it a try πŸ™‚

ruben.hamers 2020-07-03T03:50:42.254200Z

alright, damn; finally fixed it. my formdata and request setup was correct, except for my headers.. I set

{:content-type "multipart/form-data"}
explicitly. So, for other readers, now my re-frame ajax handler looks like:
(rf/reg-event-fx
  :request-files-upload
  (fn-traced [{:keys [db event]} [_ a]]
             (let [params (second event)
                   url (str (:backend-url environment) "/files/upload")]
               (println params)
               {:http-xhrio {:method           :post
                             :uri              url
                             :timeout          8000
                             :response-format  (ajax/json-response-format {:keywords? true})
                             :body params
                             :on-success       [::good-files-upload-result]
                             :on-failure       [::bad-files-upload-result]}})))
Thx for the help @p-himik

πŸ‘ 1
p-himik 2020-06-30T19:47:06.181800Z

None of this matters because we don't know what you server expects. HTTP 400 means "Bad Request" which, in turn, "indicates that the server cannot or will not process the request due to something that is perceived to be a client error".

p-himik 2020-06-30T19:47:25.182Z

So it mostly depends on your server and its configuration.

p-himik 2020-06-30T19:51:13.182200Z

Ah, I misunderstood the purpose of the topmost code block. Seems like you're sending your server upload-file but it expects file.

p-himik 2020-06-30T19:53:33.182400Z

Or maybe I'm misunderstanding it again (sorry, a bit late here). It wants a multipart request from you, but you're sending it a JSON request - maybe that's the issue.

knubie 2020-06-30T23:01:58.183100Z

I think it’s request animation frame

2020-06-30T23:53:15.183300Z

yep

knubie 2020-06-30T23:58:03.183500Z

what exactly does that mean though?