Fork me on GitHub
#reagent
<
2017-02-02
>
jfntn01:02:36

Hi folks, I'm looking for a way to make a reaction from a vector ratom in order to run a fn for every element added to the vector, is this possible?

conaw17:02:13

jfntn: I’m not sure exactly sure this is what you’re asking for, but have you seen reagent/track?

jfntn17:02:30

@U0VMXH0H1 thanks, didn’t see this one and it looks pretty close. If I understand the docstring correctly this does provide an abstraction for re-running a fn when an ratom changes, but what seems to be missing here for our use-case (and throught the reagent api AFAICT) is an efficient way to get at the novelty for the tracked ratom.

jfntn17:02:26

Right so in your example, the key distinction in what we’d like to achieve is that you wouldn’t reduce the entire atom every time, but you ‘d get the “sum so far” and the new value, and call + with that. Essentially go from linear to constant time updates

conaw17:02:28

yeah, have you thought about adding an add-watch?

conaw17:02:10

I haven’t run into a situation where performance is a bottleneck personally so haven’t needed this

jfntn17:02:08

I think it’d be the same situation with add-watch right?

conaw17:02:52

you could probably do this with a make-reaction, re-frame had some examples of making reactions that were dependent on other atoms

jfntn17:02:01

Our app is basically one giant vector of events and a number of reductions that could be performed incrementally when new events are conj’ed so that would be a huge perf win

conaw17:02:16

yeah, I’m doing the same thing

conaw17:02:21

with firebase

conaw17:02:59

my guess would be to have two atoms, one with all the events, one with the final state of the reduction

conaw17:02:19

and maybe put a timestamp or some other incrementer on the events

jfntn17:02:21

As far as I understand the problem now, yes that sounds like that’s the way to go

conaw17:02:07

yeah, basically I have a current app-state and an event-log, and a set of all the items that have been transacted into the current app-state

jfntn17:02:17

It seems like if we wanted this to work in our ideal case we’d need some sort of ratom compatible stream that’d have the values being conjed

conaw17:02:21

and I just only transact the changes that haven’t been added yet

jfntn17:02:33

Right, the way I see it working now is to have different atoms (or pieces of state in the db with re-frame) to represent the different reductions, and transactions need to make sure to update all the accumulators

jfntn17:02:01

Which is certainly not as nice as doing the mutation in one place and having the reductions update themselves

conaw17:02:32

You’re basically doing Redux in cljs right?

jfntn17:02:48

Actually I’m not familiar with reduc

conaw17:02:33

nah, I mean from vanilla javascript land

jfntn17:02:43

I mentionned streams and transducers here, so yeah that sounds like it

jfntn17:02:54

thanks I’ll read that

jfntn17:02:19

There’s a lot to like in reagent/re-frame though, so I’d rather find a way to make it work in that context

jfntn17:02:41

Seems like if we could make a core.async chan into a ratom we could make it work

conaw17:02:44

yeah yeah, I wouldn’t suggest going exactly with that approach

conaw17:02:12

why not just have a channel update a ratom and use tracks to have data flow from there

conaw17:02:39

You might like the zetawar sourcecode

conaw17:02:52

it’s where I got that deftrack macro from

conaw17:02:06

he has a nice way of handling events too

conaw17:02:34

similar to re-frame’s dispatch, but might be helpful to you

jfntn17:02:46

Oh I wasn’t aware of that project, I’ll take a look

conaw17:02:53

either way, would love to hear if you’re able to get any big perf benefits

jfntn17:02:29

Datascript def looks like a good match, but we were kind put off by a bunch of people reporting how they hit a perf wall with it

conaw17:02:33

You know when they hit that wall. I’ve found it to be fantastic.

jfntn01:02:37

To be more accurate, the use case is a step-by-step reduction with a reducing function

cfleming02:02:03

Hi all, I’m finding that when my page first loads, there’s a noticeable delay before the JS loads and the app is rendered into my div. If I put some static content in the div like a spinner or something, will reagent overwrite it the first time it updates?

gadfly36103:02:53

@cfleming yeah, you can do something like this:

<!doctype html>
<html lang="en">
  <head>
    <meta charset='utf-8'>


    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.7/components/loader.min.css"
	  rel="stylesheet"
	  type="text/css">
    
  </head>
  <body>
    <div id="app">
      <div class="ui active inverted dimmer">
	<div class="ui text loader">Loading</div>
      </div>
    </div>

    <script src="js/compiled/app.js"></script>
    <script>foobar.core.main();</script>
  </body>
</html>

gadfly36103:02:03

The loader will show until the js loads

cfleming03:02:39

@gadfly361 Great, thanks. I guess the same would apply if I pre-rendered the initial state of my app and hard-coded that in there, right?

gadfly36103:02:57

@cfleming yeah, that should also work. if you go thru the trouble of pre-rendering your initial state, i find putting the initial state in to a var (something like state = { ... }) pretty useful when done with something like this in cljs

(defonce js-state
  (try
    (js->clj js/state :keywordize-keys true)
    (catch js/Error e {})))

(defonce default-state
  { ... })


(defonce app-state
  (reagent/atom
   (merge js-state
          default-state)))

cfleming03:02:40

So what is that doing? I don’t understand the js/state part.

gadfly36103:02:48

if you are able to have a backend dynamically create an index.html file for you, then you can add a var to the head of the index.html ... my example above would assume something like this:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript">
      var is_dev =  true ;
      var state = { ... };
    </script>
  </head>
  <body>
    <div id="app"></div>
    <script src="js/app.js"></script>
    <script>foo.core.main();</script>
  </body>
</html>

cfleming03:02:13

Ah, I see, that makes sense.

gadfly36103:02:17

helps with page reload and wanting the page to come with some state

pesterhazy09:02:07

@gadfly361 I like the loading spinner!

pesterhazy09:02:14

it's cool that they provide the spinner as a separate css file

gadfly36109:02:46

Yeah, i do enjoy that semantic ui gives you the option to cherry-pick its components

borkdude10:02:43

Is it possible to completely re-mount a component when an argument changes? This would greatly simplify my component.

borkdude10:02:01

The argument is a ratom in my case.

kauko10:02:05

sounds like something that goes against the core ideas of react tbh

pesterhazy10:02:47

no that's not how react works

pesterhazy10:02:25

however, when props change the component-will-receive-props lifecycle method fires

pesterhazy10:02:13

you can performs side effects in that method

kauko10:02:14

and remember that for the argument ratom to "change", you need to give a completely different ratom

kauko10:02:17

not just change the contents

pesterhazy10:02:16

if you want to react on changes to the ratom itself, you can use a trick

pesterhazy10:02:51

(defn outter [] (inner @ratom)) (defn inner [data] ...)

pesterhazy11:02:24

then inner can react to component-will-receive-props when the ratom changes

borkdude16:02:40

Yes, it’s counter React, but I have some local state that depends on the atom. When the atom changes (and it is a completely different ratom, that’s what I’m sure of), the local state has to be adapted. Now I get around this via workarounds which are not so nice.

borkdude16:02:38

Btw I think it’s incorrect @kauko @pesterhazy. When you pass the same atom with different contents, it will trigger a re-render.

(defn component-with-atom-arg [a]
  [:div
   [:pre (pr-str @a)]])

(defn parent []
  (let [a (ratom 1)]
    (fn []
      [:div
       [:button {:on-click #(swap! a inc)}
        "Click me"]
       [component-with-atom-arg a]])))

pesterhazy16:02:32

@borkdude I sounds like we're talking past each other

borkdude16:02:05

probably 🙂

borkdude16:02:40

ah, about component-will-receive-props, that is probably true yeah

pesterhazy16:02:04

of course component-with-atom-arg will be re-rendered -- the ratom changes

borkdude16:02:39

I’m not sure what I would do with in the side effect to re-mount the component though

pesterhazy16:02:23

you don't manually re-mount components in react; it's not possible

pesterhazy16:02:51

an updated component should be equivalent to a remounted one

borkdude16:02:45

yes, I agree with those statements, but it’s not how this component works unfortunately

borkdude16:02:33

(defn component [state]
  (let [local-state (ratom (:foo @state))]
    (fn [state]
      [:p @local-state])))
I have something like this, but more complex. state is a provided/shared Reagent atom. local-state is also updated in the body of the component. When you get a different state, local-state doesn’t contain the right information anymore.

borkdude16:02:39

But when you define the local-state in the inner body, that obviously doesn’t work either.

pesterhazy16:02:14

yup you'll need to use :component-will-receive-props to update local-state when the state prop changes

pesterhazy16:02:52

or I guess when state's contents change?

pesterhazy16:02:03

in which case the wrapper component mentioned above might help

pesterhazy16:02:31

in any case it seems like a relatively convoluted setup, maybe you can find a simpler path to your goal

borkdude17:02:45

I have a workaround, but it isn’t pretty.

borkdude17:02:34

I finally discovered what happened. My component gets properly unmounted, but it gets initialized twice, first time ‘wrong’ because of some async behavior