Fork me on GitHub
#fulcro
<
2018-01-02
>
tony.kay18:01:43

@tpliliang Your question is kind of difficult to understand. I think maybe you’re saying you have something from a database you’d like to use as the data model for a dropdown?

tony.kay18:01:50

@levitanong I’m not sure I’d worry about it. Even the link you gave says: “You can avoid this by defining the ref callback as a bound method on the class, but note that it shouldn’t matter in most cases.”

tony.kay18:01:44

you could save the bound function as an attribute on the instance, then it would be the same thing every time (for that instance)

levitanong18:01:04

@tony.kay Haha, I’ve actually run into a weird infinite loop with it. It seems to rerun when fulcro rerenders the component. My workaround was to add a guard to check if the ref is nil, but I think this points to a deeper issue. how would one save it as an attribute on the instance?

wilkerlucio18:01:15

@levitanong if you are wondering where to do that, I can think on 2 places: 1. on initLocalState. 2. part of a custom factory method

wilkerlucio18:01:37

on any of those places, you can try to use (gobj/set this "someHandler" (fn []))

tony.kay18:01:42

why not componentWillMount?

wilkerlucio18:01:14

I was thinking this is like a constructor, so a component might be mounted and umounted many times, but it's only constructed once

tony.kay18:01:50

but for the purpose of ref, it only matters at all in the context of render. mount/umount will call it again anyhow so it doesn’t matter

wilkerlucio18:01:53

but in practice it shouldn't make much a difference I guess

tony.kay18:01:58

you need to close over the instance

wilkerlucio18:01:10

yeah, all of then should work fine 🙂

tony.kay18:01:24

I just don’t remember if the this in componentWillMount is the same thing that you get once it is mounted

tony.kay18:01:28

I assume it is

wilkerlucio18:01:38

I think that's correct, but have to try

tony.kay18:01:46

exactly…but that’s what I’d try first

levitanong18:01:48

thanks for the help, guys!

levitanong19:01:47

worked like a charm, guys! thanks again. 😄

wilkerlucio19:01:29

cool, which hook point you used to save the handler?

levitanong19:01:40

componentWIllMount. seemed like least effort to me

wilkerlucio19:01:34

fair enough 🙂

tony.kay19:01:16

@levitanong Thanks for letting us know

tony.kay19:01:38

@levitanong so, I assume what you wrote is a lot more code that a simple anon function. What’s your reasoning? I’m curious because I just started tinkering with the card to see what it would take to make that work well, and it is a lot more confusing, a lot more code, and the only thing it seems to save is an extra call to a function that probably takes microseconds to run.

levitanong02:01:51

Curiosity :P it wasn't actually a lot more code because the anon function contained a check on the presence of an extant reference. Without it, I was getting a blown stack.

roklenarcic21:01:46

hm why does forms init-form overwrite existing values with their defaults?

roklenarcic21:01:53

it didn't used to

tony.kay21:01:11

Forms hasn’t changed in a very long time

tony.kay21:01:19

you thinking build-form ?

roklenarcic21:01:24

nope init-form

tony.kay21:01:51

I have not touched that code since Untangled

tony.kay21:01:56

(that I remember at least)

roklenarcic21:01:49

Well I'm printing out a specific entity

tony.kay21:01:57

init-forms should not be re-initializing anything though

roklenarcic21:01:01

and I can distinctly see a boolean being flipped

tony.kay21:01:02

you are right there

tony.kay21:01:47

could be a bug

tony.kay21:01:58

basically init-one and init-many call back to init-form*, which in turn checks for the form state key

tony.kay21:01:00

I don’t see that it should be doing anything at all to an initialized form

roklenarcic21:01:11

I'll try to isolate the thing

tony.kay21:01:28

but it does walk the whole form set, so a subform, for example, would get initialized if it were not already

tony.kay21:01:49

in other words the top-level of a form set does not short-circuit the traversal

roklenarcic21:01:17

well I have a value in a subform position already

tony.kay21:01:30

a value, or form control data?

tony.kay21:01:38

they are two different things

roklenarcic21:01:55

an entity with no form control data yet

tony.kay21:01:03

then it would initialize that one for sure

roklenarcic21:01:50

yes I understand initializing the form control, but why flip the existing boolean data on subform entity to default

tony.kay21:01:17

ah, I see. You’re saying it should be initializing the form control data, but it is affecting the entity state

tony.kay21:01:32

did you nest swap!?

roklenarcic21:01:44

not sure what you mean by that

tony.kay21:01:55

did you call a function from swap that itself calls a swap

tony.kay21:01:21

(defn f [state-atom]
   (swap! state-atom ...))

(defn g [state-atom] 
   (swap! state-atom f))

tony.kay21:01:24

that kind of thing

roklenarcic21:01:50

My functions work with state, not state atom

tony.kay21:01:55

darn…was going for the “wild ass guess award”

roklenarcic21:01:09

I'll make a new project and see if I can replicate

tony.kay21:01:20

so, the init form code doesn’t touch entity state

roklenarcic21:01:25

I've had a couple of things misteriously break after migrating to 2.0

tony.kay21:01:56

the reason I made the guess I made is that perhaps you have one stage that is making a change, and then something is reverting it by accident. A nested swap is a common way to accidentally cause that

roklenarcic21:01:24

admittedly my form/modal mutations and components are complicated as hell

roklenarcic21:01:44

so I'll try to find a nice example

tony.kay21:01:46

OK, so that could be a serious indicator of a problem with something internal to Fulcro…Very interested in knowing about regressions

tony.kay21:01:30

So, let me help a little more

tony.kay21:01:52

You’re looking at app state directly?

tony.kay21:01:06

i.e. you’re not deriving your statement from UI appearance?

roklenarcic21:01:39

No. My mutation was -> sequence of normal function operating on state

roklenarcic21:01:55

and then I interposed

roklenarcic21:01:59

print statements

tony.kay21:01:14

and the state is changing with the call to init-form??

roklenarcic21:01:30

and I noticed that the boolean flips when init-form fn is called

roklenarcic21:01:36

the fn, not the mutation

tony.kay21:01:59

that is very odd

tony.kay21:01:54

So, is the field you’re talking about a form field of the subform that was not yet initialized?

tony.kay21:01:09

i.e. the subform was not initialized

roklenarcic21:01:38

I'm working on making a new project that will demonstrate that

roklenarcic21:01:46

If I am able to demonstrate that

roklenarcic21:01:59

I still suspect I'm screwing it up somewhere

tony.kay21:01:59

ok, so, init-form does make sure your entity has values on all fields that are declared

tony.kay21:01:15

and that could be buggy with respect to booleans

tony.kay21:01:27

but it has not changed in a long time

roklenarcic21:01:47

yes the issue is with a boolean field(checkbox) that has initial value of false

roklenarcic21:01:51

default in form is true

tony.kay21:01:00

that is it then

roklenarcic21:01:02

so init-form overrides it to true

tony.kay21:01:14

yeah, that’s a bug it sounds like…let me look…hold on

tony.kay21:01:31

but this regressed? Or is this just maybe you noticing it in a new case?

roklenarcic21:01:33

maybe you are using an if to check if a prop is nil, but if also works on false same as nil

tony.kay21:01:57

yeah, that is the bug

tony.kay21:01:08

just a sec, I’ll push a snapshot fix

roklenarcic21:01:48

the false/nil duality is quite a tricky thing

tony.kay21:01:25

Yeah, get bit by that one periodically

tony.kay21:01:00

easy fix, and I had test coverage that tested multiple types…just not boolean 😕 Lesson learned on testing Clojure…boolean is the most important type to test 🙂

tony.kay21:01:12

2.1.0-SNAPSHOT on clojars with the fix

roklenarcic21:01:19

then I have a much trickier bug (or possibly my mistake) -> sometimes transactions break my router and the props don't get passed down to the :current-route component

roklenarcic21:01:44

I used inspect to look at the thing

tony.kay21:01:52

the only other change in that release right now is the rendering refactor that eliminates the need for react-key. Let me know if you see rendering weirdness

roklenarcic21:01:49

and db is fine, my root component and router props is fine, but the component that my router points to has empty props. But that only changes when I do a transaction, the initial load is fine.

roklenarcic21:01:03

And it doesn't happen just with a particular transaction.

roklenarcic21:01:38

I'll probably need to make an example of that one, since it happens on some transactions but not on other

tony.kay21:01:37

so, I would be interested in hearing what happens if you try 2.1.0-SNAPSHOT with an option change on that: Pass {:render-mode :keyframe} as :reconciler-options to your new client.

roklenarcic21:01:18

what's that for?

tony.kay21:01:33

rendering initial frame is done from root. Refresh (on transactions by default) are done from the component using an ident query. I’d be interested in seeing if that changes it.

tony.kay21:01:40

That is a brand new option I just added

tony.kay21:01:55

It means “always render every frame as a key frame”….which means from root.

roklenarcic21:01:40

otherwise with the latest version there's quite a bit of flickering when using bootstrap Collapse

tony.kay21:01:51

flickering?

tony.kay21:01:15

that shouldn’t be. The animation is done by the browser

roklenarcic21:01:04

I have a panel with 10 items in it, then 10 more items in a collapse, when I expand the collapse, the items outside of it flicker, (transaction target is the panel enclosing both groups), and it didn't happen in 1.0

roklenarcic21:01:22

I'll try the keyframe render now

tony.kay21:01:49

So, rendering in 2.0 is nearly a complete rewrite from Om Next. Are you doing anything (via timeouts) while the collapse is happening?

roklenarcic22:01:11

but I see the collapse fires a bunch of timeouts with swaps in them

tony.kay22:01:41

ah….yeah, that still should not cause flicker, because no data has changed

tony.kay22:01:01

it will cause extra renders, but they should be no-ops that are short-circuited by React

tony.kay22:01:27

I would suspect that flicker is caused by unstable keys causing react to throw away DOM and re-insert it

tony.kay22:01:51

you’re using stable :key values, right?

roklenarcic22:01:20

I use them where I get warnings

tony.kay22:01:25

Om Next always did render from root on such swaps…so there should be no difference there

tony.kay22:01:39

yes, but what value do you give them?

roklenarcic22:01:22

Usually ID of the entity, but sometimes if I don't have that, I use a keyword if a single entity, or index of the element in the sequence

tony.kay22:01:14

ok, that should be stable. Flicker would indicate a react unmount/mount. The only reason it would do that is if: 1. Your state temporarily changed so that the linkage made them disappear, then changed back to make them appear 2. The :key of a parent (even root) changes. That will cause the entire subtree at that key to unmount/remount We use this “trick” with react-key at root to force a re-render of the app on locale changes.

tony.kay22:01:22

2.1.0 is removing that hack.

tony.kay22:01:58

So, put log messages in your :componentWillMount lifecycle in a few strategic spots and see if you can see umount/mounts

roklenarcic22:01:03

it's gotta be number 1

tony.kay22:01:29

easy to figure out which it is…then figure out why

roklenarcic22:01:34

because I added print sentence into ident function of the whole panel that contains the collapse

roklenarcic22:01:46

and it shows that opening the collapse

tony.kay22:01:50

ident is called for all sorts of reasons

roklenarcic22:01:04

ident gets called 8 times

roklenarcic22:01:11

4 times with normal props

roklenarcic22:01:16

2 times with empty props

tony.kay22:01:20

you want to see props in render, and you want to see mount/umount.

roklenarcic22:01:22

then 2 times with normal props

tony.kay22:01:40

so, not sure what you’re doing exactly, but empty props coming to ident can be bad…probably mean you’re rendering something in a parent even though it isn’t there.

tony.kay22:01:52

e.g. you query for it and always render, but it isn’t always in db

tony.kay22:01:00

you should make that rendering conditional

tony.kay22:01:21

rendering a missing thing could cause unstable keys if the keys are based on the props

tony.kay22:01:28

(and the props are missing)

roklenarcic22:01:04

that is probably related to that problem I mentioned earlier

roklenarcic22:01:28

after some mutations I get empty props on components directly under a router

roklenarcic22:01:04

I put print into the render. Opening the collapse it gets called 3 times. Empty props, then twice with normal props

tony.kay22:01:15

Yeah, that would cause flicker 🙂

tony.kay22:01:36

it is getting called 3 times because collapse swaps 3x

roklenarcic22:01:42

I tried using inspect to figure out what's wrong

roklenarcic22:01:16

but DB is fine and root props are fine, but the component directly under the router sometimes gets empty props

tony.kay22:01:17

OH…wait…do you have ident defined on the router targets, or just the router?

tony.kay22:01:34

is it consistent?

tony.kay22:01:03

because on component refresh the behavior is different with 2.0 for sure

tony.kay22:01:00

so, that could have caused “breakage” that would have been harder to notice in 1.x

tony.kay22:01:19

(your program could still have broken in weird ways, just not as easily on render)

roklenarcic22:01:29

I have

(route/defrouter MainRouter :main-router
  (ident [this props] [(:ui/component-id props) :singleton])
  :people People
then in component People I have.
[this {:keys [ui/component-id ui/collapse people/all-people]}]
  {:query [:ui/component-id {:people/all-people (fp/get-query Person)} {:ui/collapse (fp/get-query b/Collapse)}]
   :ident [:ui-components/by-id :ui/component-id]

tony.kay22:01:57

nope, that’s wrong

tony.kay22:01:17

the ident function on the router must generate the correct ident for any member

tony.kay22:01:31

It is not the ident of the router…it is the ident of the children

tony.kay22:01:49

the ident of the router is based on it’s ID and is done for you

roklenarcic22:01:54

right but all children have idents [:ui-components/by-id x]

tony.kay22:01:06

who has :singleton?

tony.kay22:01:37

the ident has to be the, well, the ident 🙂

roklenarcic22:01:20

but why does it work on inital render

tony.kay22:01:51

because the ident is only used for normalization and optimization of refresh

tony.kay22:01:19

:keyframe will “fix” that, and 1.x would have worked with it as long as you didn’t try to normalize things with it.

tony.kay22:01:39

it’s just broken in several ways…you happened to find a way to make it “seem to work ”

roklenarcic22:01:46

I am using keyframe and I still have the "occasionally empty props" problem

roklenarcic22:01:50

but I will fix the ident

tony.kay22:01:20

yeah, fix the ident….that will clear up your problems. drop the keyframe. It is really just for curiosity and isolating possible bugs

roklenarcic22:01:46

oh yeah, this fixed everything

roklenarcic22:01:50

dropped the keyframe and it works

tony.kay22:01:21

great, glad to hear it

tony.kay22:01:35

UI refresh is optimized by running ident-based queries to refresh sub-trees. If your ident is wrong, things won’t work well. In 1.x, the rendering used the path from root to optimize render. in 2.x we rely only on idents for refresh. This is much simpler internally, but will expose broken ident functions.

roklenarcic23:01:53

Speaking of idents, do they have to be a pair of keywords? What if I have a component that represents a link between two entities

tony.kay23:01:00

an ident is always a keyword first, the second element can be anything that supports equality comparison

tony.kay23:01:32

The only reason you’d have such a component is if it has some representation beyond the linkage (such as abstracting the linkage into a higher-level concept)

tony.kay23:01:07

Router is a perfect example. It is a thing whose “state” is an ident. (current-route)

tony.kay23:01:41

it also mixes in a union, so it can select which query to use to continue down the graph efficiently…

tony.kay23:01:07

and unions, of course, require that the first element of the ident used in the linkage matches the key for the query to use in the union