Fork me on GitHub

I have a Layer 3 subscription serving a vector of things. I subscribe to this vector in a view. The view shuffles it. I have a button which I want to trigger a rerender. As it is now, this button only dispatches data into the Layer 1 subscription that is a signal to that Layer 3 one. So when the data changes, the view rerenders. But if the dispatched data is the same as before, I get no rerender. (The reason I want a rerender is that shuffling, if that wasn’t clear.) Anyone who understands what I am trying to describe? Any idea on how to solve it?


I'm no expert, but "the view shuffles it" sounds a bit like a problem. Have you considered doing the shuffling in an event handler and storing the result of the shuffle (I'm assuming you get a sequence of indexis or something like that) in app-db?

🙏 2

Exactly - if you want shuffling to be done as a reaction to some other data change, the shuffling has to be done in a subscription to which that data change is visible. Or in an event handler, if that makes more sense. Also, you probably meant layer 2 instead of layer 1 above - since layer 1 is the app-db itself.

🙏 2

Thanks! Yes, Layer 2. 😃


Success. Thanks again, both of you! It feels a bit wrong to do the calculations in the event handler, but it also makes sense not to replicate the input-text as I did before. I’ll probably get over the “feels wrong”. 😃

👍 1

I'm curious, why does it feel wrong?


It feels right to have raw data in the db and use the subscriptions to create “views” from the data.


You can still use a sub to create a shuffled data. And shuffling will be done on each value change - not on each re-render.


(reg-sub ::data
  (fn [db _]
    (:data db)))

(reg-sub ::shuffled-data
  :<- [::data]
  (fn [data _]
    (shuffle data)))


Yes, but I want the button to reshuffle also when the data hasn’t changed.


What’s the concensus these days about impure subscriptions (ie. using some source of randomness or the current time)?


Hmm, good point, not a great thing to have in there then. But same would apply to an event handler as well since we still want to keep both subscription and event handlers pure.


Yeah, at least with event handlers you can supply them as coeffects


A wild idea - have the button update the seed and store it in the app-db. Then use some way to generate stable shuffle using that seed. Of course, it can't be shuffle then because it doesn't support seeds, I think.


So it'll become

(reg-sub ::shuffled-data
  :<- [::data]
  :<- [::seed]
  (fn [[data seed] _]
    (shuffle-with-seed data seed)))


It was me thinking to keep the subs pure that got me to do the shuffle in the view. Rationale: Let the thing that requires shuffle take on the shame of impurity. 😃


Yeah, that makes sense. But ideally, views also shouldn't process the data themselves. So the seed solution seems right now like the best of both worlds. The coeffects solution is another viable one, but it will definitely be more clunky.


Wilder idea that I actually entertained a while.

(reg-sub ::shuffled-data
  :<- [::data]
  :<- [::random-number]
  (fn [[data _] _]
    (shuffle data)))


Obviously the correct solution is to require the user to submit the random seed, he/she can deal with the impurity troll

😂 1

Nah, make the user shuffle the data.


Reminded me of this masterpiece:


@U0ETXRFEW Just in case - the difference between our wild ideas is that my sub handler is pure, which is what re-frame advocates. That said, your solution will also work - it just has downsides, which you might or might not care about.


My app isn’t for real use. Just an exercise I want to do together with some other folks. So I stayed away from my wild idea, because don’t want to spread a bad pattern.

👍 1

Im not sure if that fits in the pure world much but my instinct would have been to create a local state (atom) derived from the re-frame state which can be directly re-shuffled by a button click :thinking_face:


I want it to stay stable between re-renders. Maybe that’s possible with a Form 2 component and local atom?


It is possible.


So, I am trying this idea now, I think I like it. However, on hot reload it resets to be non-shuffled. I’ve tried a couple of different things, but I can’t stop the subscription from resetting the atom on re-render….


Yep, because hot-reload causes not just a re-render but a re-evaluation of the code. defonce is used to prevent that - re-frame's app-db uses it, that's why the state is preserved upon code reloading. And subs preserve their values as long as their handler functions are pure.


Oh, so then it is not possible really with a local atom? (Since I really meant hot reload and not just re-render).


Indeed, not possible.


It’s session to inspire REPL usage I am planning so can’t have that! 😃 But I can use defonce. That works and lets me keep the shuffling outside the db, events and subs.


FWIW, I would still try to put the result of the shuffling inside app-db. Everything can still be kept pure if you move all the random stuff into its own coeffect.


There is actually a good case for using a seed like you “suggested”, @U2FRKM4TW. I just now, in trying out the resulting app, wanted to get a particular shuffle back…


I’ll investigate the coeffect track a bit because I’d like to learn about it. But I really need be a bit closer to my comfort zone during the session.


A coeffect is basically something that re-frame calls before handling a particular event - just to provide the event handler with additional data. When you use ref-event-fx, that db that you receive is a default coeffect.

Daniel Lundsgaard Skovenborg08:09:24

Hi, I've just migrated a small app to re-frame and it has been such a delight to work with! Then I tested my production bundle. Even with simple optimizations instead of advanced optimizations I get a Uncaught TypeError: yl is undefined. Is this a known issue using re-frame with compiler optimizations which just requires some additional configuration? I couldn't find anything on this but my searching skills might just have failed.


Do you use JS interop anywhere in the new code?

Daniel Lundsgaard Skovenborg10:09:08

Sorry, I must have done something weird with my setup. Even with everything in my core outcommented I still get the error. I even tried to delete all other source files.


Purge all cache and try again?

Daniel Lundsgaard Skovenborg13:09:43

Purged browser cache, cleared node_modules, package-lock.json, M2-cache. Still the same. Here's the line that goes wrong with advanced optimizations:

        bi = yi.unstable_cancelCallback,
Best variable name ever 😂

Daniel Lundsgaard Skovenborg13:09:56

I'll try to compare with a previous version of my code.


What exactly is wrong in those lines?

Daniel Lundsgaard Skovenborg14:09:57

Uncaught TypeError: yi is undefined. So the n.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED exists but the Scheduler doesn't.


Weird indeed. If you create a minimum reproducible example, I can take a look.

Daniel Lundsgaard Skovenborg14:09:43

Thanks 🙂 It will probably not be today.


I'm in no hurry. :)

Daniel Lundsgaard Skovenborg19:09:43

I found the problem and the solution! I could break my latest production just by adding re-frame as a dependency in Leiningen. Then it occurred to my that just below that line I had my dependency on reagent-material-ui from whose examples I learned that if you import React through NPM dependencies you must exclude the Clojars dependency. So, the dependency needs to be:

[re-frame "1.2.0" :exclusions [cljsjs/react cljsjs/react-dom cljsjs/react-dom-server]]

Daniel Lundsgaard Skovenborg19:09:28

But thanks a lot for your time, I'm glad to use a library with a welcoming community 🙂

Daniel Lundsgaard Skovenborg19:09:41

I should probably file a report for the documentation.


Ah, alright, glad you have figured it out. FWIW, I forgot pretty much about all NPM/cljsjs problems after switching to shadow-cljs.

clojure-spin 1
Daniel Lundsgaard Skovenborg12:09:09

Ok, I'm using Figwheel right now.