Fork me on GitHub
#re-frame
<
2021-07-01
>
Daniel Craig03:07:45

How can I translate this CSS into a map that re-frame understands? font-family: 'Lato', sans-serif;

p-himik07:07:26

Re-frame doesn't deal with CSS, so you probably mean Reagent. Just a regular string should work: {:font-family "'Lato', sans-serif"}. But I haven't tested it.

dvingo16:07:34

In a re-frame app, there are some subscriptions (and in turn, the views that deref those subscriptions) that are dependent on server-side data. This brings up the problem that this server data needs to be fetched in order for the view to work. Thus the problem is: I want to use a component which requires possibly dispatching an event coincident with it being rendered, and having this event be dispatched if the data is missing. I'm wondering if there are any patterns or tools available to make this happen seamlessly. What I'm imagining is that there would be a decorator on top of subscribe that would associate some re-frame events to dispatch if the return value of a subscription is a value indicating missing data (perhaps just nil) , this way the human using a component doesn't need to remember to dispatch the events to fetch the requisite data. Some prior art I know of is the apollo client's useQuery function: https://www.apollographql.com/docs/react/data/queries/ the difference would be passing in a subscription instead of a graphql query.

dvingo17:07:29

Thanks for the reply, however, I politely refute that these are relevant to my request. 1. I don't want to poll. 2. I don't want to dispatch manually based on some "user intent". As you can see in useQuery there is no event, the data fetch happens transparently.

p-himik17:07:35

While I can see how polling is not what you want, dispatching "manually" is the way that's consistent with the whole re-frame approach. With global interceptors, you don't have to tie dispatches directly to vies - that's why I put "manually" in quotes above. You use a flat in app-db and monitor that flag both in a view and in a global interceptor - when that flag is set to some predefined value, the view is displayed and the interceptor dispatches the data fetching event. If you don't care about why such an approach is good and why re-frame docs recommend it, you can check out the link in the "First, What Not To Do" section on the polling FAQ page.

👍 2
dvingo17:07:10

Right, was just going to followup with the details stated in, the "why we don't load in on-mount" leads here: https://purelyfunctional.tv/article/react-vs-re-frame/#Reacters-load-data which states: > So how do we solve that same problem, which is to tie a component with its data source? Well, one way to do it is to tie the display of a component to the fetching of data within an Event. and conveniently leaves off the hairy detail of what actually causes the dispatch of this event, which is the core of the problem. If we follow the logic prescribed in "First, What Not To Do", the pattern of dispatching based on the user intent, which in this case happens via a URL route change, we end up having to know the mapping of every component to its corresponding data dependencies and which re-frame events those data dependencies we need to fire. This is incredibly brittle: • If I add a new route I have to track all components and figure out which events to dispatch, this list of components may not always be statically determined. • If I use a component which has server data dependencies inside a new route, again I have to remember to dispatch the appropriate event.

dvingo17:07:42

So it seems to me that the answer of why not to do this, is just "because we said so" and smells of dogmatism trumping pragmatism

dvingo18:07:59

The global interceptor idea is interesting though, I'm curious how it would scale to an entire application

dvingo18:07:32

If you have any sample code I think that would help me understand what this would look like in practice

p-himik18:07:12

Ah, that "what not to do" document has changed drastically since the last time I read through it. Not sure where to find the original now. Or maybe the document I'm thinking of is at a completely different place and I just mixed things up. In any case, code samples that I wanted to refer you to were using reg-sub-raw with dispatch inside. So whenever you subscribe to something for the first time, that subscription would dispatch an appropriate event for data fetching, and then serve that data when it becomes available. > So it seems to me that the answer of why not to do this, is just "because we said so" and smells of dogmatism trumping pragmatism Re-frame has a very specific philosophy behind it, which is described incredibly well in its documentation. That philosophy is pragmatic, given the problems re-frame is trying to solve. Making re-frame load data "somewhere" in response to anything that's not an event goes against that philosophy. When you circumvent subscriptions and events mechanisms, you lose all what re-frame brings to the table in that particular case. At that point, it's better to extract a component as a Reagent one and then embed it into a re-frame app. Sample code:

(reg-event-db :show-panel
  (fn [db [_ panel-id]]
    (assoc-in db [:panels panel-id :visible?] true)))

(reg-global-interceptor
  (->interceptor
    :id :load-data-for-panel
    :after (fn [ctx]
             (let [old-db (rf/get-coeffect context :db)
                   new-db (rf/get-effect context :db)]
               ... Find all panels for which `:visible?` switched from false to true ...
               ... For each such panel, dispatch an even that loads data for that panel ...))))

p-himik18:07:39

Found it, and yes - it's a different page altogether. But notice that it also says that the document will be retired and that one should probably not read it. https://day8.github.io/re-frame/Subscribing-To-External-Data/

dvingo18:07:46

Thanks for the detailed reply - I totally understand the core philosophy of one-way data flow. I'm wondering how to operate within this framework/philosophy to solve the problem: I want to render a component that has data dependencies which require events being dispatched - is it possible to just render this view and have it work without me needing to manually dispatch anything? I'm wondering if this desire is against the philosophy though. I guess I'm imagining some data structure that would describe these dependencies, and then some machinery that would deal with resolving them via events, but I suppose that's what the interceptor example is demonstrating.

p-himik19:07:00

> is it possible to just render this view and have it work without me needing to manually dispatch anything? In re-frame, a view isn't rendered on its own - it's rendered as a reaction to some data change. Data change is made via events. Ergo, to show a view, you have to dispatch an event. Among the consequences of that event might be data fetching, when you need it. There are various ways to implement it, global interceptors - one such way and IMO the most flexible one.

dvingo19:07:38

This all makes sense, I'll have to think it over some more and maybe try implementing this interceptor pattern.

isak21:07:02

FWIW, we have disregarded this advice in our app, and it hasn't become an issue except for the cases where that data needs to be interactive (e.g., edited in a form). For those cases we use events instead. So to be clear, we do have subscriptions that kick off requests.

lilactown17:07:03

FWIW modern React advice is also to start fetching as soon as possible - i.e. on navigation, rather than component mount

p-himik17:07:59

And if you're writing a browser, you may start fetching even before any navigation. :D

isak14:07:57

Yea we do that too sometimes, and sometimes even start loading data before any ClojureScript is evaluated at all. But in general I agree with what dvingo said above: it is cleaner architecturally if the data/query a component needs is located with the component. That is harder to achieve if it isn't tied to the component lifecycle. I guess the downside is the request will be started ~1 frame later than it otherwise would.

dvingo17:07:17

Something like:

@(network-subscribe [::my-subscription] [::fetch-data-event])
where if the data is missing at the given sub, the event is dispatched to fetch it

hkjels07:07:12

(if-let [the-val @(subscribe [::the-val])]
  [:div the-val]
  (dispatch [::retrieve-the-val]))