Fork me on GitHub
#fulcro
<
2019-09-28
>
tony.kay01:09:06

More videos coming to the YouTube playlist. One on dynamic UI driven by data, and two on UI state machines. The first UI state machine one is a bit longer than usual, but I didn’t see a great way to break it up. The second one is just the final few minutes of what I probably should have just done in the first one…but I’m not up for video editing, so I’ll just post it as 2.

36
grant18:09:28

@tony.kay In section 3.4.2 of the Fulcro 3 book, http://book.fulcrologic.com/fulcro3/#_shadow_cljs_compiler_configuration, the code listing has port 8000 and the text has 8080 for dev-http. I doubt anyone would be confused by the typo, but I figure I'd point it out. I wasn't sure it was worth a PR, but if you want one I can.

tony.kay18:09:08

you sure it isn’t referring to a running server instead of cljs dev?

mss18:09:59

having trouble triggering a refresh after a load. a component is querying for a link at the root of my app db which is a list of idents pointing to a different table in my db. e.g. my component queries for user/active-things which is composed of [thing/id thing-id] idents pointing to the thing/id table. I’m dispatching loads to update the relevant [thing/id thing-id] idents, but they aren’t triggering a ui refresh in my component despite the app state being updated my load! call looks like:

(df/load! mounted-app [:thing/id thing-id] Thing {:refresh [:user/active-things]})
any pointers for how to debug?

mss19:09:21

f3, on 3.0.1

mss19:09:31

using the default renderer

tony.kay19:09:13

:user/active-things is a list of idents?

tony.kay19:09:41

unless the list itself changes, that won’t refresh the components using active-things

mss19:09:33

huh that’s interesting. would have figured that an ident in that list getting updated would cause a re-render for a component querying for :user/active-things

tony.kay19:09:52

your explicit refresh should do it, though

tony.kay19:09:11

the ident isn’t being updated…the target table entry is

tony.kay19:09:14

there’s a diff

mss19:09:17

ah yes you’re right

mss19:09:32

but like you said the explicit refresh should trigger an update

tony.kay19:09:40

but if that component was on-screen, it should refresh wihtout any help at all

mss19:09:02

yep and it is

tony.kay19:09:50

so, you have [:user/active-things '_] in some component query, and it then renders everything from that table?

mss19:09:09

something like {[:user/active-things '_] [:thing/id :thing/some-prop]}

tony.kay19:09:32

Then you’re using a proper component to render each thing?

mss19:09:05

technically getting fed into and rendered in a child but the parent’s data isn’t updating either

tony.kay19:09:07

does it ever refresh correctly?

mss19:09:23

yeah if I trigger any other ui update that causes a re-render (e.g. toggle some field on the component)

tony.kay19:09:55

but if it is onscreen and you load, no reffresh?

tony.kay19:09:05

(of the specific thing)

tony.kay19:09:13

not talking about adding/removing from table

tony.kay19:09:41

and have you reloaded your browser?

mss19:09:46

yep I’m just updating a thing/field via that load! and the component is not getting updated

mss19:09:15

yes have reloaded my browser. no hot reload shenanigans involved afaict

tony.kay19:09:58

So, that’s rather weird. The component itself should be updated no matter what

mss19:09:00

don’t know why this would affect things, but the load! is getting dispatched inside of a setInterval fn set up in my app’s client-did-mount cb

tony.kay19:09:27

should not matter

tony.kay19:09:57

let me see the code for the Thing and the component rendering them…not so much all the DOM, just the query/ident/options stuff

tony.kay19:09:22

and how you’re rendering the list itself

mss19:09:25

sure, give me a second to chop things up

mss19:09:30

appreciate the help

tony.kay19:09:20

I’m most interested in your Thing component

mss19:09:43

so the example I mentioned above was obviously a little contrived. here’s the gist of what I’m doing: TestRun and TestRunTest are query components I use to create my initial query and do post-render load!s. Project is actually doing the rendering here, and doesn’t get refreshed post-`load!`

(defsc TestRunTest [this props]
       {:query [:test/id]
        :ident (fn [] [:tests/by-id (:test/id props)])})

(defsc TestRun [this props]
       {:query [:test-run/id
                :test-run/progress
                {:test-run/test (comp/get-query TestRunTest)}]
        :ident :test-run/id})

(defsc Project [this props]
       {:ident         (fn [] [:pages/by-id :project])
        :route-segment ["project"]
        :will-enter    (fn [_ _] (dr/route-immediate [:pages/by-id :project]))
        :will-leave    (fn [_ _] true)
        :initial-state (fn [params]
                         {:ui/project-page.tests-filter-bar-sort-selection "Test name A-Z"})

        :query         [:ui/project-page.tests-filter-bar-sort-selection
                        {[:current-user/active-test-runs '_] [{:test-run/test [:test/id]}
                                                              :test-run/progress]}]}
       ; if I log (:current-user/active-test-runs props) here, the data is not refreshed post load
       (map test-run-cards (:current-user/active-test-runs props)))

:client-did-mount (fn [mounted-app]
                    (dr/initialize! mounted-app)
                    (js/setInterval (fn []
                                      (let [app-state @(::fulcro-app/state-atom mounted-app)
                                            active-test-runs (compute-active-test-runs app-state)]
                                        (doseq [test-run active-test-runs]
                                          (let [test-run-id (:test-run/id test-run)]
                                            (df/load! mounted-app [:test-run/id test-run-id] TestRun {:refresh [:current-user/active-test-runs]})))))))

tony.kay19:09:06

ok, so first off, your refresh should not be needed since you’re not actually changing that list…Project is never going to update from that load, because that list didn’t change. That is correct behavior.

tony.kay19:09:27

however, any on-screen TestRun component should auto-refresh when any props change in the db

tony.kay19:09:43

(so if you log in those, you’d see those update without updating Project)

tony.kay19:09:28

I don’t see you using a proper factory for rendering TestRun…what is test-run-cards?

mss19:09:45

so the components using that TestRun data passed down from the Project don’t actually have an ident/query that’s composed into root – it’s passed in from the parent

tony.kay19:09:49

and why didn’t you use the component itself in the query of Project

tony.kay19:09:10

then they won’t refresh

tony.kay19:09:20

because you’re not doing it right 🙂

mss19:09:32

makes sense 😂

tony.kay19:09:34

targeted refresh works based on idents

tony.kay19:09:44

ehen a component mounts, it adds itself to a refresh index

tony.kay19:09:48

based on it’s ident

tony.kay19:09:54

no component, no index, no refresh

mss19:09:32

I went down that route because each of the cards that was getting rendered had ui data that needed coordinating by the parent. someone had advised me that the easier way to do that would be to have the parent component (i.e. the Project component) manage the state and pass everything down to simple factory components

tony.kay19:09:20

coordination from the parent should happen through computed props, generally (if it is computed data)

tony.kay19:09:08

I would generally also not use a link query for that list, since I would think the Project component itself probably needs a private to-many edge

tony.kay19:09:20

(load! app :project/active-test-runs TestRun {:target [...project ident... :project/test-runs]})

tony.kay19:09:09

might be useful to watch

mss19:09:13

that all makes sense. I guess what wasn’t clicking for me before and the reason I went down this frankenpath: each child of Project is basically a component that takes a test-run-id and renders some dom with some computed state passed in. I couldn’t really wrap my head around how to compose the query/ident for that child card component, as well as how to compose that into the parent Project

mss19:09:00

going through a project/test-runs route does make more sense

mss20:09:31

I don’t know if any of what I just wrote makes sense. feel like my mental model of how rendering works is a little borked. really appreciate you coming down this rabbit hole and helping me out. am diving into that video now

tony.kay20:09:04

There’s a 1-to-1 with card and Run it seems

tony.kay20:09:24

so, you can model a Card AS a Run

tony.kay20:09:29

give it the same ident

tony.kay20:09:51

when you want to render it as a Card, use the Card component…when rendering as a Run, render it using the other one

tony.kay20:09:53

that kind of thing

mss20:09:10

oh. that’s extremely simple and straightforward 😂

tony.kay20:09:20

usu. is 🙂