Fork me on GitHub
#re-frame
<
2018-06-17
>
kennytilton00:06:15

I cannot find anything on how to do a progress bar during a long-running task in re-frame. I thought processing a chunk in one event-fx and returning (a) an updated DB with new counts and (b) kicking off a new event to process another chunk would do it, but I am not seeing the progress element redisplayed until all chunks finish. Should that work, or do I need to kick off events with setInterval or requestAnimationFrame or some such?

kennytilton00:06:34

Tempted to get the DOM node and update it directly…

kennytilton00:06:02

No luck, with or w/out animationFrame. The later all run at once after the task completes. 😞

lwhorton01:06:45

i’m not exactly sure what you’re trying to do, but reframe is just for managing state complexity. what do you mean by ‘long running task’? is there some single event that takes a long period of time for reframe to process (not recommended)? or some task on the server takes a while to process, and you want to show in the UI a nice progress indicator?

lwhorton01:06:31

if you want to show a progress bar at 25/50/75/100%, just dispatch a new event for each change to the state. the impetus for the state change is up to you — a sse, websocket notification, interval/timer callback, whatever.

lwhorton01:06:11

if you are talking about some browser-side task that takes a really long time, check out https://github.com/Day8/re-frame/blob/master/docs/Solve-the-CPU-hog-problem.md

lwhorton01:06:52

… but i’ve written a lot of reframe apps and have never run into this issue. if you can’t get the work done in under a frame (16ms), its probably too much work for a browser and should be offloaded to the server

kennytilton01:06:14

My app loads 1 to 4 pages of HN Who’s Hiring answers into iframes for parsing, and can find over 800 jobs. The pages are already curled from HN and sitting in resource/public/files. I am processing chunk by chunk quite nicely by kicking off a new event as one completes (updating db along the way, but the progress bar does not change until the last event completes.

kennytilton01:06:35

My JS version works fine, working off requestAnimationFrame to process each chunk.

kennytilton01:06:20

No surprise there since I am giving up control to the browser after each chunk.

lwhorton01:06:25

If you are getting control back from the browser every RAF, reframe has time to process DB updates and reagent has time to flush rerenders. are you sure that you are using the subscription mechanism properly and derefing the slice of state that represents the progress bar’s percentage?

kennytilton01:06:29

When I was trying RAF they all ran after the event chain completed, so that ruse went nowhere — does re-frame itself handle an event and any events spawned by that even in its own RAF?

lwhorton01:06:14

how are you chaining together the processing chunks? using the {:dispatch [:next]} mechanism of a reg-event-fx?

kennytilton01:06:26

Mebbe I’ll fork re-frame and dig into the internals,

kennytilton01:06:08

Yep, {:db <new counts> :dispatch <same event>}

lwhorton01:06:35

i would setup a minimum case to make sure the problem isn’t lying elsewhere. i’m not sure about a handler re-dispatching a call to itself, but I see no reason why it wouldn’t work. to me it smells like there’s just a bad subscription, or bad db update, or a bad deref happening somewhere.

lwhorton01:06:57

sorry i can’t be more help, little tough when i can’t see all the relevant code

kennytilton01:06:16

I did begin by simply setting the pg bar values to make sure I knew how that worked, and at the end it does update to 100% from 0% instantaneously, and I am seeing print statements indicating the work moving along in steps, and I even see my direct maniulatio of the DOm attributes honored as we go — just no redraw! I think it deeply significant that all the RAFs get kicked off at the end. Methinks re-frame keeps processing the event queue until it is empty, so the browser does not get control until all work is done.

lwhorton01:06:54

^:flush-dom metadata, though i’m not convinced that’s your problem

kennytilton02:06:23

Oooh! Excellent and promising. I remember reading that a while ago.

mikerod02:06:29

@U0PUGPSFR there is a :dispatch-later

mikerod02:06:54

So you don’t stay stuck in one event queue cycle

mikerod02:06:18

Its implementing in the browser via JS setTimeout I believe.

kennytilton02:06:21

I will check that out too! Excellent. Thx all.

kennytilton02:06:49

Yer a genius! Works like a charm.

mikerod02:06:26

Hah nice. Yeah I haven’t done a progress bar in re-frame but I think I could think of how to do one utilizing :dispatch-later

kennytilton02:06:23

I am being gross and updating the DOM directly but I am sure subscribing to the DB values I am already driving would work. I will give it a go.

kennytilton02:06:29

Yep, works great by the book.

kennytilton02:06:25

But is the doc @U0W0JDY4C referenced accurate? That just has a dispatch of the same event, which is what I was doing. OTOH, as Luke suggested the flush-dom meta data also works! So now I have two solutions! I guess I like flush-dom so I do not wit unnecessarily.

mikerod03:06:33

Just read the cpu hog post. It’s a good one. Not even thinking it’d require dispatch later. Just can’t take over cpu in a long running handler.

mikerod03:06:44

Or flush dom meta could work

kennytilton02:06:58

Next Q, and I think I know the answer, and I am fairly sure this is a problem with React: must we always make a new component to effect any UI change? The progress bar element is fine, it just needs a new value at each step. Fuggedabout it? Zap it and make a new one? Just want to be sure. [EDIT: I was wrong. See thread. If attributes are only change, react just does attributes.]

mikerod03:06:41

Not sure I understand this question. Reagent will try to re-render components (by calling Your component fn) when its input vals have changed. The render results are hiccup that are converted to React elements which describe the React Virtual DOM tree

mikerod03:06:13

React will then do it’s diff algorithm to try to find out what to update on the real DOM and how to do it efficiently.

kennytilton10:06:42

Ah, OK, so just because I see my function to nake the Hiccup called does not mean React ends up swapping in a whole new progress element on each update of the value. I’ll go re-read the doc on their diffing. Thx.

mikerod13:06:26

Yep. It should work out more efficiently than that. Would be mutations to the same existing DOM node

kennytilton13:06:41

Cool: “When comparing two React DOM elements of the same type, React looks at the attributes of both, keeps the same underlying DOM node, and only updates the changed attributes. For example:” https://reactjs.org/docs/reconciliation.html

mikerod14:06:31

I recommend this post. Pretty short and gives an impl that is easy to read that shows how React works conceptually.

mikerod14:06:48

It is obviously simplified. React is much more involved.

kennytilton16:06:56

Thanks! I think I have seen that before. The good news is that I have been building frameworks like this myself so it is easy to grok. Look for tag-dom-create in here where my framework “dom” becomes real dom: https://github.com/kennytilton/mxweb/blob/master/cljs/webmx/src/tiltontec/webmx/html.cljs

kennytilton16:06:56

I do not say “virtual dom” because my approach is so much different.

mikerod22:06:38

Interesting. I’ll check it out when at laptop

kennytilton00:06:44

Oh, it’s just the obvious. Take a map proxy for a Dom element and build the real deal. I have a bit of life cycle as well, and rigorous event -> mutate -> side-effect cascade. Nothing new, and no surprise: we are all working on the same problem, and the problem determined the solution.

Drew Verlee13:06:19

Has anyone tried using https://github.com/nodename/stately? Care to share your experiences?

hoopes00:06:37

I tried, and really wanted to like it, but it seems half-finished (with no disrespect to the author). There’s also https://github.com/jiangts/re-state which is slightly more re-frame friendly, but still didn’t get super far with it. I feel like there’s definitely a big opportunity for a statechart lib with re-frame, but haven’t put much effort into it.

Drew Verlee01:06:45

hmm. Well, maybe this is something i can pick up or build.

Drew Verlee01:06:36

What issues did you run into with the library?

Drew Verlee01:06:46

I feel like it has all the reasearch i could need on the subject.

Drew Verlee01:06:11

It’s possible the idea is simple enough that its best to just wrap it around the context of the problem

Drew Verlee01:06:32

as in, you apply statecharts to your problem not your problem to statecharts

Drew Verlee01:06:06

@U0XE6F84R hi! It looks like you forked stately. I think statecharts might be a great way to organize high level application logic and so wanted to give that idea a spin using stately, but its not clear what limitations the lib has. Can you weigh in on whats left to be be done? Why you forked? What your experience with state charts has been like so far?

hoopes15:06:17

Sorry for late response - I think it was a matter (probably my failing) to not understand what exactly statecharts were meant to be used for. The sample apps in the stately lib were ios heavy, and the documentation didn't help me get over the hump. I was trying to use it for front-end routing, but couldn't get all the wires to connect (I still think it's valuable to know what route you're leaving 🙂 ).

Drew Verlee15:06:16

@U0G2E3SEM I suppose it’s important to understand the goal, a system that is easier to understand and expand to meet some specification. I think statecharts/machines are a helpful mental model because they reduce and limit the model to a bare minimal set of things. I think they can employed in the small (for a just one part of your application) and at a system wide level. In both cases, i think they primary serve as a form of communication and organization. The difficultly is probably in deciding what level of abstraction their going to rest at. I think to successful employ them, you need to fix that abstraction level and not mix it. Good: Off — flip switch ---> ON ON — flip switch —> OFF BAD Off — flip switch — > update database --> OFF Mixing in lower levels (db udpates) and the logic (on/off) expands the scope to much and makes it hard to understand how things are supposed to transistion.

Drew Verlee15:06:59

I think it would be interesting to see an example where the different layers could compose and layers though. Like statecharts but only with different charts tagged so that the visibility could be toggled.

Drew Verlee15:06:34

so that a client could view the parts it wanted. “show me the business logic” hmm ok. Now impose on the auth logic.. ok i see the issue.

hoopes15:06:01

ya, my 2 test use cases were "any time i enter some high level state (state A), check auth, and reject if necessary" and "if i jump from some child of state A to a child of state B, detect that, and start/stop something" - i was trying to scope it just to routing, but i think i understand using state charts for the state of other things, like models in the system (account map in the global db, for instance...)

hoopes15:06:26

when you say "mixing logic" do you mean "updating the db within a transition"? Or you don't think a state transition is a good place to dispatch an event to update the db?

Drew Verlee17:06:18

@U0G2E3SEM. I’m just theorizing, based on what i find slows me down when trying to model things this way, that… you need to have a well defined sense of what your going to model and restrict it to just that. It would be more confusing to put everything in a statemachine/chart because i think the semantics are too restricted/confined. Thats why i think successful usecases are ones with a well defined and understood domain. Or one that needs it (like a game).

Drew Verlee17:06:20

I think its ok to update a db, i just think that an action at that level shouldn’t be mixed with something higher up, like business logic.

hoopes17:06:34

do you think frontend routing doesn't lend itself to a statechart? I feel like URL paths <-> statecharts are pretty related, no?

hoopes17:06:57

i mean...you can squint and imagine you can talk about them with statechart language, right?

Drew Verlee21:06:48

I think routes are a good fit if you think its useful to view your application that way. I’l leaning towards modeling more of the business logic and less of the implementation details (routing, etc…) but you can do both, its just a matter of time, effort and organization i think.

kennytilton16:06:51

Style Q (only?): I have an app that uses a nested map with a half-dozen properties to describe/monitor a long-running task, viz. month-load-task. Often a reg-sub :month-load-phase, eg, just does (get-in db [:month-load-task :phase]). Fine, but now I am mindlessly coding up all these trivial reg-subs. Or I could have one reg-sub :month-load-property that takes the property as a parameter. Yes, this exposes the nature of the :month-load-task -- is that the only objection?

mikerod22:06:57

Sounds fine to me. I think it is worse when you start trying to get to clever and pass db path info into subs. A single key Param doesn’t seem to crazy. I have definitely had subs like it to a degree

kennytilton00:06:26

OK, crickets. I get that a lot. 🙂

kennytilton09:06:42

Sorry @U0LK1552A, I had not seen your reply. It would be interesting to see how many <sub/reg-sub pairs could be coded without thinking. Abstraction is cool when it hides something (a) synthetic hence (b) can easily change, but when I just want that damn property of that damn structure!… this is what macros are all about, making boilerplate go away, automating our coding at a higher level.

Bravi22:06:19

hi everyone. is there a reason why secretary/dispatch! wouldn’t be updating the url? or is it like that by design?