Fork me on GitHub
#re-frame
<
2016-05-04
>
rohit15:05:14

I am trying to understand how atoms and reactions work. In the readme for re-frame, I read that (in section How Flow Happens in Reagent), "The magic thing about a reaction is that the computation it wraps will be automatically re-run whenever 'its inputs' change, producing a new output (return) value."

rohit15:05:29

But my experience in the repl is something else

rohit15:05:04

what I am seeing is that the computation is rerun only when I deref the reaction

rohit15:05:28

(require '[reagent.core :as r])
(require-macros '[reagent.ratom :refer [reaction]])

(def app-db  (r/atom {:a 1}))

(def ratom2  (reaction 
                    (do
                        (println "running")
                        {:b (:a @app-db)})))

rohit15:05:59

can anyone clarify this?

hugobessaa16:05:20

That's exactly the way it should be

hugobessaa16:05:46

Reagent just rerun reactions when ratoms they deref change

hugobessaa16:05:02

the "input" is the deref of an ratom

rohit17:05:06

@hugobessaa: thanks. what I am seeing is that when I perform a reset! on app-db (as in the readme), the ratom2 doesn’t get updated till I deref it.

rohit17:05:38

the readme suggests that its automatic. or maybe thats what i understood incorrectly

rohit17:05:02

from the doc - "this change to app-db, triggers re-computation of ratom2"

hugobessaa17:05:46

reagent uses this as a performance improvement. if you do not deref a ratom, it means you're not interested in it's value and so do not recompute it.

rohit17:05:38

@hugobessaa: that makes sense to me. but i am struggling to figure out why the readme of re-frame suggests automatic computation..

hugobessaa17:05:58

> Well, the computation is just a block of code, and if that code dereferences one or more ratoms, it will be automatically re-run (recomputing a new return value) whenever any of these dereferenced ratoms change.

hugobessaa17:05:41

this paragraph makes it very clear that re computations are done when you deref a ratom. you think it could be more explicit?

roberto18:05:32

if it derefs more than one atom n times simulatenously, does it recompute n times?

rohit18:05:50

@hugobessaa: thanks. i think my confusion arises from the fact that in this context, which ratom being derefed would cause the computation to happen. so its the deref of the ratom returned from the reaction.

rohit18:05:00

@roberto: atleast in my example, if i deref the ratom2, 2 times, i see ”running” printed twice suggesting to me that code is run twice.

roberto18:05:30

yeah, I ask because sometimes I deref the same atom at the same time in multiple locations

rohit18:05:49

@roberto: i think we are missing something

rohit18:05:05

there is some other machinery at play (my wild guess)

roberto18:05:01

I appreciate re-frame’s docs, but I also have issues understanding ratom once I start building an app that needs to deref the same ratom in multiple locations.

roberto18:05:29

ratom itself might need an entire readme

rohit18:05:07

@roberto: agreed. my unscientific and uneducated hunch is that the real power of the ratom is only realised when used inside a component

hugobessaa18:05:19

or inside a reaction. something that is even less documented

hugobessaa18:05:56

the reagent readme just gives an overview of it's power and functionality

hugobessaa18:05:19

I'm starting to realize we should help reagent to document better it things.

hugobessaa18:05:34

A nice thing to do after the 0.6.0 release that is closeby

fasiha19:05:23

Does reaction return an ratom?

rohit19:05:49

@fasiha: i think it returns something which is essentially the same thing as ratom in the sense that it implements all the same protocols as ratom (and more)

fasiha19:05:35

Suppose I have a Type-II re-frame component, something like:

(defn my-component [] 
  (let [reaction-1 (subscribe [:sub1])
        reaction-2 (subscribe [:sub2])] 
    (fn [] [:div @reaction-1 @reaction-2])))
and say reaction-1 is always getting updated because the sub1 subscription returns a frequently-changing piece of app-db, but reaction-2 and sub2 change very infrequently. Am I correct in understanding that when my component gets re-rendered because of a change in reaction-1, the subscription to sub2 will NOT get called—or if it is called, the subscription function will notice that that piece of app-db hasn't changed since last time and will not run the reaction?

hugobessaa19:05:01

The type II component creates these reactions from subscribe when the component is created to be rendered (the first time probably)

fasiha19:05:32

I have a fancy sub2, i.e., a reaction-inside-a-reaction:

(re-frame/register-sub 
  :sub2 
  (fn [db] 
    (let [parameter-reaction (reaction (:path @db))]
      (reaction (EXPENSIVE-FUNCTION @parameter-reaction)))))

hugobessaa19:05:32

it's important to note that the reaction holds the value from a computation

fasiha19:05:58

Ah, ok, so I don't have to worry about anything.

hugobessaa19:05:59

it do not run the computation once per deref

hugobessaa19:05:46

it run when creating with the (reaction) macro and when a ratom you deref inside it changes

fasiha19:05:10

Ahh, that's much clearer, thanks @hugobessaa !

hugobessaa19:05:48

actually I'm not even sure if the (reaction) executes it's computation when it is created

hugobessaa19:05:12

maybe just when you deref it for the first time. but I'm not sure

fasiha19:05:37

So what happens if my subscription creates a first reaction, then derefs it not in another reaction, and then returns a second reaction?

fasiha19:05:18

I.e.,

(let [reaction-1 (reaction (:path @db))
      first-value (first @reaction-1)] 
  (reaction (inc first-value)))

fasiha19:05:54

first-value will get a "cached" copy of (:path db) if it's not changed, and the rest is as normal?

hugobessaa19:05:04

fasiha, I got bad news for a reaction with expensive computation

hugobessaa19:05:32

I tested a reaction inside the REPL

hugobessaa19:05:55

it will run the computation everytime it is derefed

fasiha19:05:12

This is a reaction that doesn't contain any other reactions, right?

hugobessaa19:05:52

ok, so I did more testing inside the REPL

hugobessaa19:05:41

answering your first question: it will run the computation of a reaction everytime it is derefed

hugobessaa19:05:11

answering your last question: a reaction without a deref inside will return the same value everytime you deref it

hugobessaa19:05:22

test that in a REPL

hugobessaa19:05:07

you could write the last code as:

hugobessaa19:05:32

(let [reaction-1 (reaction (:path @db))
      first-value (reaction (first @reaction-1))] 
  (reaction (inc @first-value)))

hugobessaa19:05:08

so the returned reaction would work as expected

hugobessaa19:05:43

these tests remembers me I need to dig deeper into the inner workings of reaction and r/atom simple_smile

fasiha20:05:59

Thanks for the explanation @hugobessaa that's definitely helpful. I do need to internalize the rules about when things get rerun thru experimentation—usually just "knowing" the rules isn't enough

mikethompson22:05:47

@rohit: the readme was not intended as a tight specification document, it was mostly about giving mental models: https://www.reddit.com/r/Clojure/comments/4h1fv4/a_technical_overview_of_arachne/d2s0a6w And if you are using 0.6.0, remember also that Dan has done recent work on reaction, so I'm not sure where we are currently around this, except to say it has changed with this release.

jmmk22:05:35

Anyone checked out https://github.com/mobxjs/mobx? It brings mutability back into the mix but it's pretty interesting. Seems like fewer moving parts and easier to get started with than redux, et al. The way the observable state is wired up might be an interesting alternative to manual subscriptions in re-frame