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.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])] ...)
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.`
}]/>
In clojurescript, I do this, and it works fine:
[:> Tour
{...
:steps [{:selector "...", :content "..."}]}]
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>,
}]/>
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?
Use reagent.core/as-element
.
thanks! that worked π
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.
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}
That would never have worked.
Ahh
Perhaps if you substitute reg-sub
for reg-sub-raw
?
OK, it's register-sub in the code I'm trying to re-engineer
Fabulous π
Thanks so much Mike
So register-sub
is deprecated for reg-sub
and reg-sub-raw
?
https://github.com/day8/re-frame/blob/master/src/re_frame/core.cljc#L778-L783
It will still work, but you'll get a depricated warning
You should just rename to reg-sub-raw
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.
or rewrite slightly and use reg-sub
(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
What I was looking for, but not what I needed
Ooh OK. That's much more understandable
thanks. I was getting hung up on trying to understand why it needed an explicit call to reaction
Thanks for the help !
Hey, that's what I was looking for π
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?
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.
So if you can't use subscribe in the main body of reg-sub, how can you achieve what I described in 2?
But what if I need to subscribe to a subscription, whose parameter depends on some other value I obtain from another subscription.
Say, in my case, a link has two endpoints and only need to depend on their positions and not on position of all units.
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.
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?
No downsides. As to your question about feeding sub value into another sub, you can just use reg-sub-raw
for that.
Ah, sorry, I missed the fact that you deref inside the body of a subscription and not a view. Here, I'm not sure.
So basically the correct way would be to use reg-sub-raw. I will take a look at it how it works precisely.
> 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.
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
I think reg-sub-raw
is more advanced, so it is not in the main docs. But better to ask @mikethompson
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
quick question: when looking at traces
in re-frame10x, what is the :raf
operation?
I am familiar with this level of description, but I would like to get a more advanced insight.
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]}})))
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
"}]})
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
.
That's how I do it.
ok, ill give it a try π
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-himikNone 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".
So it mostly depends on your server and its configuration.
Ah, I misunderstood the purpose of the topmost code block.
Seems like you're sending your server upload-file
but it expects file
.
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.
I think itβs request animation frame
yep
what exactly does that mean though?