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
Oliver George 2021-06-15T01:44:54.121800Z

We don't have an "offline first" approach to our webapp. It's essentially a nice interface to a REST API. Refreshing the browser bootstraps the app which fetches any data it needs. Our data model is complicated. This approach would generate a lot of requests except that we've provided a "pull api" mode which amounts to a poor-mans GraphQL. Goal is saving query count (at the cost of complicating bakend db queries)

rberger 2021-06-15T02:16:25.122Z

@services The need to render a static view for social media crawlers is a real pain in the butt when doing a SPA. As you discovered, there is no way to have the meta tags be generated on the page by javascript, the crawlers won’t run the javascript. It requires some form of SSR at least for requests from the crawlers. Its even more painful if the SPA is being served up from something like AWS Cloudfront. I spent a week iterating trying to make the SPA work with social links until I learnt that its not possible. Ended up doing a rather painful implementation with lambda@edge that detects if the request is from one of the social crawler bots and effectively SSR renders a custom page with just the header / meta tags and the minimum content (images primarily). Hopefully a blog post someday. Since you are using nginx/apache, you can just have the routes in there look at the user-agent for crawlers and send it to the service route to generate the special page, which can be simple hiccup like https://gist.github.com/rberger/b2b484e6a4af8b029e8f2d776b31bbb2 And for non crawler routes just go to your normal path to server your SPA.

oliver 2021-06-15T07:20:50.122200Z

Good summary of the problem… and interesting ideas. The project at hand being a hobby I'm under no pressure to make this work; just found it to be a good occasion to take a first stab at SSR. As I've said: I've already gotten this to work: 1. Endpoint receives a request containing app state (as b64 query-param) 2. Set Re-frame to that state (via multiple dispatches) 3. Render SVG-component via reagent.dom.server/render-to-string 4. Convert SVG to PNG and save to disk 5. Redirect to the static PNG (with 302) – works with crawlers. Steps 2–4 are skipped if an image for that particular state already exists. My only remaining worry is about concurret requests: What if another request comes in during 2. Could the DB-Atom be reset by handler B dipatching against it while handler A is still building the state it wants to render. Whith a global DB atom I smell trouble, but this is hard to test – maybe there would be no issue. With my current project there wouldn't be, since I get a couple of hunded reuqests per day at best – but in principle? If I ran this on an actual server rather than a shared hosting limited to a single node runtime I could just add another node build, that starts no server at all but only takes the encoded state and spits the PNG to disk. The handlers would then just shell out, run that node script and then redirect to the resulting PNG. This would separate the Re-frame instances – but, again, I'd like to find something more elegant, even if it's less brutal than firing up Puppeteer all the time.

wombawomba 2021-06-15T09:45:05.128500Z

I'm trying to call document.querySelector("#my-id").scrollIntoView() for a url that goes to a different view (from /my-view to /my-other-view#my-id) by dispatching two effects (`[:navigate-to "my-other-view"]` followed by [:scroll-to "my-id"]). Unfortunately this doesn't work because the :scroll-to event is fired before the corresponding element exists (both events seem to fire as a batch, before the view is rendered). Is there a way to ensure that the :scroll-to effect doesn't get applied until after the view has been re-rendered?

p-himik 2021-06-15T10:40:40.128800Z

Re-frame acts on a queue - no handlers are run in threads. So no handlers can run in parallel. However, workflows can run concurrently if they consist of multiple events - when handling an event results in dispatching another event via some means.

p-himik 2021-06-15T10:41:23.129Z

Yes. By not using the effect and instead calling scrollIntoView inside the component itself.

p-himik 2021-06-15T10:41:39.129200Z

I would 100% not use re-frame machinery for this particular task.

wombawomba 2021-06-15T10:41:55.129400Z

Yeah I think that's what I'll end up having to do

p-himik 2021-06-15T10:42:15.129600Z

And, of course, before calling the scroll function you would have to check that the URL fragment points to the relevant view.

wombawomba 2021-06-15T10:43:06.129800Z

I had this solution in place for moving around an already loaded page (where it works fine), and was hoping for a quick fix so I wouldn't have to redesign it right away

p-himik 2021-06-15T10:44:12.130Z

I've been in your shoes before, and I don't think there's a proper fix here. There's a dirty workaround - just firing up setInterval and checking if the element exists.

wombawomba 2021-06-15T10:54:20.130200Z

Yeah okay, thanks 🙂

2021-06-15T14:23:19.131200Z

I have a question on http-fx. So here: https://github.com/day8/re-frame-http-fx#step-3a-handling-on-success I have to write a global callback for each http call? Seems quite clumsy?

p-himik 2021-06-15T14:24:58.131400Z

You're using that fx in an event handler that's also available globally. Same thing.

2021-06-15T14:39:08.131600Z

This comes clumsy when

(defn some-func []
  (call-http-1)
  (use-the above-http-result)
  (call-http-2 with the above transformed result))
So we have to write lots of call backs here for a specific purpose.

p-himik 2021-06-15T14:40:29.131800Z

Zero clue what you mean with that code, to be honest.

2021-06-15T14:41:43.132Z

(defn some-func []
  (-> (call-http-1)
      (use-the-above-http-result)
      (call-http-2 with the above transformed result)))

2021-06-15T14:42:27.132200Z

Maybe more direct. So if I want to make several http requests with the later depending on the further. It is a little bit verbose to go with the (dispath [:response-handler]) approach.

p-himik 2021-06-15T14:47:33.132400Z

Events in re-frame are in general indeed a bit more verbose when you need to chain then. That's a given, regardless of whether you're using re-frame-http-fx or not. There are libraries that try to alleviate that, like https://github.com/ingesolvoll/re-chain for example.

2021-06-15T14:51:16.132700Z

Thanks. It’s a quite niche package. How do you get to know that?

p-himik 2021-06-15T14:52:21.132900Z

You mean, how I found that library? No clue. I just hoard and catalog all links that I see that seem to be useful. So probably someone mentioned it here before.

p-himik 2021-06-15T14:52:50.133100Z

Also, the same guy is behind kee-frame, which I use - so maybe that's how I know about it.

2021-06-15T15:58:44.133300Z

@zackteo, we have found that a functional reactive approach can be very effective when dealing with mutable JS view objects like a Leaflet object: 1. Model all aspects of your Leaflet map as immutable data in your db. If you want to have multiple maps on the page, your db model has to support multiple model instances. 2. Bind your Leaflet object to the relevant dom node when the view component is created 3. Produce a subscription to your map's immutable data 4. Create a reagent reaction that updates the JS Leaflet object with the immutable data as input. There are two important restrictions: 1) your immutable data is your only input to the reaction 2) the only side-effects of the reaction are changes to your mutable JS object. Your Leaflet object effectively becomes a materialized view of your app db. The only way to make changes to your mutable Leaflet object is by transforming the immutable data in the app db. Reagent/re-frame will take care of keeping your mutable JS in sync with your app db.

2021-06-15T19:47:32.134200Z

alas, re-frame-10x doesn't work in a sandboxed iframe, which is part of my (admittedly unusual) app architecture.

1
2021-06-15T19:54:09.135100Z

is there any hope of making that a soft requirement, maybe with degraded functionality? or is it vital to how 10x works?