Fork me on GitHub
#reagent
<
2018-06-04
>
vishnuvp11:06:42

I was trying to generate dynamic number of checkboxes, the state of which is stored in the app state which is an atom which is a list of dicts like this [{:checked false, :text "Sample text 1"} {:checked false, :text "Sample text 2"} {:checked false, :text "Sample text 3"}] The function below is expected to generate a checkbox corresponding to the specified index of app’s db (`db`). The function does it jobs and the checkboxes are clickable.

(defn gen-checkbox [index db] 
     [re-com/checkbox
            :label (:text (@db index))
            :model (:checked (@db index))
            :on-change #((swap! db assoc-in [index :checked] (not(:checked (@db index)))))
     ])
However, I get this error when I click on any checkbox. Uncaught Error: Assert failed: Reaction is read only; on-set is not allowed The error occurs at swap!. Can some one point out where I am doing wrong?

mikethompson11:06:50

:on-change #((swap! db assoc-in [index :checked] (not(:checked (@db index))))) should be :on-change #(swap! db assoc-in [index :checked] (not(:checked (@db index))))

mikethompson11:06:02

Ie. Remove a set of parens

vishnuvp11:06:39

Thanks @mikethompson. However, the error is still occuring.

Uncaught Error: Assert failed: Reaction is read only; on-set is not allowed
(fn? (.-on-set a))
    at reagent.ratom.Reaction.cljs$core$IReset$_reset_BANG_$arity$2 

pesterhazy11:06:31

there are too many things going on in that snippet. I'd narrow it down to something simpler

vishnuvp11:06:16

@pesterhazy thanks! I am relatively new to clojure and reagent. Is there a better way to generate a dynamic number of checkboxes and save the states in the app’s state?

pesterhazy11:06:09

it looks fine to me

pesterhazy11:06:46

one thing we don't see is what db is here. It looks like it's not the Ratom but a Reaction

mikethompson17:06:38

@devinbox.vishnu As @pesterhazy suggests, it looks to me that your db is a readonly Reaction and not a read/write ratom

mikethompson17:06:54

In which case, your (write-full) attempt at a swap! is failing with that error.

reefersleep19:06:08

I want to set up an async polling process that runs while my reagent component is on the screen, and is removed when it isn’t. Is there a canonical example for this sort of thing? I’m guessing a form-3 component can take me at least some of the way, maybe all of the way?

justinlee19:06:12

@reefersleep you can definitely do that with form-3 by using :component-did-mount and :component-will-unmount. but i think you can also do it in a more clojurey way using with-let and finally

reefersleep19:06:46

@lee.justin.m what’s with-let? I don’t see it anywhere.

reefersleep19:06:54

Ah, I thought we were talking vanilla Clojure! reads

reefersleep19:06:28

That’s just brilliant, exactly what I need! Cheers @lee.justin.m!

gadfly36119:06:17

@reefersleep if using re-frame, can take a peak at re-pollsive https://github.com/gadfly361/re-pollsive

reefersleep19:06:17

Ah yes. I would, only this particular project is reagent + custom stuff on top, and it’s a work project 🙂

gadfly36119:06:31

Ahh gotcha 👍

reefersleep19:06:44

Cheers though!

🍻 4
reefersleep19:06:26

hmmm @lee.justin.m it’s not working how it should, but I think it’s to do with my lack of understanding of core async.

reefersleep19:06:02

I’ve with-let a call to go-loop. The finally block is called, and inside it, I use close! to kill the loop, only it doesn’t kill the loop.

reefersleep19:06:34

I was unsure of whether using close! was a correct move, since the documentation state that it works on channels, and I’m unsure of what a loop is (maybe a channel?)

reefersleep19:06:57

But I used close! to ensure that figwheel-reload wouldn’t start several loops, and that seemed to work.

reefersleep19:06:21

I probably just need to rethink my async code 🙂

justinlee20:06:56

i quit using core.async because it find its metaphors to be the wrong solution for me and it introduces a lot of complexity, so i’m the wrong guy to ask. but, generally, i recall a discussion that core.async is not cancellable (?)

justinlee20:06:48

i assume what you are doing is pumping into a channel inside of the setInterval callback?

justinlee20:06:56

consider just signaling through the standard reagent/atom mechanisms and then just cancelling the interval in the finally block. so much simpler

reefersleep07:06:52

@lee.justin.m I just realized that I could do the same thing this morning 🍻 I started off with (code that does not close the go-routine):

(reagent/with-let [my-timeout-routine (go-loop []
                                          (<! (timeout 2000))
                                          (do-my-work)
                                          (recur))]
[my-component]
(finally (close! my-timeout-routine)))
I did it differently, like this, and it works as expected:
(reagent/with-let [keep-timing-out? (atom true)
                     my-timeout-routine (go-loop []
                                          (when @keep-timing-out?
                                            (<! (timeout 2000))
                                            (do-my-work)
                                            (recur)))]
[my-component]
(finally (swap! keep-timing-out? not)))
Is this what you were thinking about, too?

justinlee14:06:27

Yes exactly.

reefersleep20:06:11

Brilliant! Thanks for the help! I’ll be sure to add with-let and finally to my arsenal of state management 🙂

zalky20:06:36

Hi all, I am curious about the caching semantics of the reagent.core/with-let macro, specifically with respect to the react will-mount lifecycle method. What I'm seeing is that the values with-let macro are evaluated before the component is mounted on the dom, but also on each browser reload. The will-mount lifecycle method however is not evaluated on every reload, just a hard refresh, when a new component is actually created. I was using with-let as a convenience over the will-mount method, but clearly there's some important, but not obvious difference.

justinlee20:06:45

I’m not sure I follow the distinction between a “browser reload” and “just a hard refresh”?

justinlee20:06:21

certainly though, they have different semantics. the lifecycle events are managed by react and with-let happens on top of all that. it would be interesting if you found a difference between a form-2 component and a with-let component

zalky20:06:14

@lee.justin.m, sorry, the way I put it was unclear, it's not a browser reload, but a code reload, such as when figwheel or a boot tasks automatically reloads code in the browser. No new components are made in this case, and so will-mount will not run. However, the values in with-let are re-evaluated.

zalky21:06:54

And the with-let semantics do indeed appear different than form 2. The values in a with-let are re-evaluated across code reloads, even when no new components are created. The context in a form 2 component is not.

justinlee21:06:10

its a bit beyond my understanding, unfortunately

justinlee21:06:34

i wonder if it has something to do with trying to enforce the finally block

reefersleep07:06:52

@lee.justin.m I just realized that I could do the same thing this morning 🍻 I started off with (code that does not close the go-routine):

(reagent/with-let [my-timeout-routine (go-loop []
                                          (<! (timeout 2000))
                                          (do-my-work)
                                          (recur))]
[my-component]
(finally (close! my-timeout-routine)))
I did it differently, like this, and it works as expected:
(reagent/with-let [keep-timing-out? (atom true)
                     my-timeout-routine (go-loop []
                                          (when @keep-timing-out?
                                            (<! (timeout 2000))
                                            (do-my-work)
                                            (recur)))]
[my-component]
(finally (swap! keep-timing-out? not)))
Is this what you were thinking about, too?