Fork me on GitHub
#reagent
<
2018-05-09
>
crinklywrappr16:05:56

I made this complicated function which returns a react component using create-class. I'm discovering that eg [inline-edit ...] in a reagent form doesn't cause the component to re-render. What would be the preferred way of writing a complicated component like this eg switch to react's component local state or just invalidate the region where i'm rendering the component? (gist here: https://gist.github.com/doubleagent/3da6d623ab5b99bd1d4a4c1b3c43b7ed)

justinlee16:05:59

@doubleagent generally, the pattern of (defn my-comp [] (let [...] (r/create-class ...))) works

justinlee16:05:19

i don’t think that pattern is the problem. there’s some other reason why it isn’t updating

justinlee17:05:34

what event is happening that causes you to believe that it should re-render but doesn’t

crinklywrappr18:05:51

@lee.justin.m I'm rendering the component like this (defn page (let [...] [:div [:inline-edit ... (:number @ticket) ... (:desc @ticket) ...]])), where ticket is a ratom. I expected that when ticket changed value then the component would re-render. Every other component emitted from the page function re-renders except this one, and one other component written in a similar style.

justinlee18:05:13

@doubleagent oh so you’re not repeating the arguments in reagent-render so when the passed arguments change, the render func is just using the initial closed-over values

crinklywrappr18:05:33

that seems to be the case

justinlee18:05:11

so just repeat the args from the outer function. then the render func will get the new values

crinklywrappr18:05:34

Repeat the args from the outer function in the render fn? eg (r/create-class {:reagent-render (fn [<args>] ...)})

justinlee18:05:49

right, where args is some subset of {:keys [id placeholder validate submit-rec debounce-time working editable hover idle shared]}

justinlee18:05:48

just to paint a picture here: reagent will call inline-edit the first time that its parent function is rendered. then it will save the result. on all subsequent re-renders, it will pass the same argument list to reagent-render. because you don’t repeat the arguments in reagent-render, those new values are not lexically visible to the render function

justinlee18:05:12

try this experiment: put a js/console.log at the very top of inline-edit and then another inside reagent-render. i think it’ll make more sense then

crinklywrappr18:05:17

I guess I'll have to move the existing (let [...]) block into the render function, and plug all the things previously defined in that block into an atom eg (defn inline-edit [<args>] (let [state (atom {})] (r/create-class {:reagent-render (fn [<args>] (let [<populate state atom>] ...)) ...})))

crinklywrappr18:05:34

Does that sound right?

crinklywrappr18:05:34

everything defined in the top-level let block is going to use the initial closed-over values

justinlee18:05:48

hm yes that’s true

justinlee18:05:35

oh i see what you are saying. actually looking at my code, that’s what I do too, hah. i merge everything into an atom in the render func

justinlee18:05:09

you can actually emulate object state that way too. the functional programmer people would probably kill me if they saw this code but yea that works

crinklywrappr18:05:54

lolol i'm finding that the reagent stuff only looks functional when it's simple

crinklywrappr18:05:12

that's like 80% of my program so it's not a big deal 😉

justinlee18:05:16

use functional where it works and makes sense. but i’ve got one big component that is inherently mutative because it wraps a canvas, so I just emulate an object by passing a state atom around

justinlee18:05:45

honestly it’s no worse than every js component in react, so i figure do what’s natural

crinklywrappr18:05:17

thanks man i'll give this a shot

crinklywrappr18:05:33

it sounds simpler than I thought it would be

justinlee18:05:49

fwiw, my big component looked just like yours in that it was a big closure. i actually moved to the state atom thing because that was so annoying to deal with. now its just a bunch of little functions that take the state atom as an argument

crinklywrappr18:05:09

that sounds nicer

justinlee18:05:52

its good because some parts don’t actually need the state atom or they only need a fraction of it--you can then just deref the atom and pass what you need, so you still get some partial decoupling and testability

rnagpal18:05:58

Is there anything like this for reagent https://github.com/storybooks/storybook ?

justinlee18:05:57

maybe devcards? i’ve never messed with either so maybe i’m mistaken

rnagpal18:05:55

dev cards aren’t so interactive

justinlee19:05:34

even with figwheel?

lilactown20:05:20

devcards, to me, seem much better from a developer standpoint

lilactown20:05:35

the integration with cljs.test is 👍

lilactown20:05:06

however, I've also used reagent + shadow-cljs + storybook together as well

crinklywrappr22:05:48

@lee.justin.m I found repeating the args in the render fn required me to determine when and how to overwrite state on entry, so instead I moved all that stuff into compoonent-will-receive-props and dumped everything into an atom like we talked about.

crinklywrappr22:05:10

not tested yet but it compiles and renders the default state correctly

justinlee22:05:05

yea i guess that should work too

crinklywrappr22:05:35

indentation is a mofo, lol

justinlee22:05:13

i mean parinfer

crinklywrappr22:05:23

using smartparens

crinklywrappr22:05:29

i mean there's too many indentation levels, lol

justinlee22:05:58

but if you want to insert a form, it is so much easier with parinfer/smartmode because it just autoindents everything below as you type

justinlee22:05:03

but yea i know what you mean