Fork me on GitHub
#fulcro
<
2019-05-21
>
tony.kay04:05:06

Update on Fulcro 3. Spent the entire day on it. I’ve got a full-stack TodoMVC running. Still a long way to go to round out APIs, but it’s nice to see it dancing. In an advanced compile build my new rendering optimizations cause fulcro to take about a half a ms to get the targeted update data for 2 of components, and the setStates that do the targeted props (in the release build of react 16.8.6) take on the order of 0.5 to 4ms. Of course, some of that setState time is element generation, but since Fulcro does those at compile time to js, those are basically a wash. It’s a simple app, but the design of the optimization should theoretically keep those times on that order even with a lot of things on screen (since it only updates the ones that really change, and that tends to be very few at a time).

👍 24
myguidingstar04:05:28

Thanks for your great work, Tony. Regarding timing, I guess you're making a perfect version of Fulcro first, then adding a backward compatible layer later, right?

👍 4
tony.kay14:05:41

yes. If you look at the general code, the main thing that “looks” different at the moment is just namespaces Here’s the TodoMVC code that is running: https://github.com/fulcrologic/fulcro3/tree/develop/src/todomvc/fulcro_todomvc

tony.kay14:05:55

Mutations, however, get a lot more powerful, and you can implement your own “load” with them, for example…see https://github.com/fulcrologic/fulcro3/blob/develop/src/todomvc/fulcro_todomvc/api.cljs#L130

tony.kay14:05:46

that is different that “mutation joins”…the mutation (I’m prob going to call them “operation”) can send ANY AST now…and get the raw result…then use the built-in merge if it wants

tony.kay14:05:49

that kind of thing

tony.kay14:05:05

So, making data_fetch API work “on top of that” will be trivial, but is necessary for source compat

tony.kay14:05:05

All the internal mess that was due to shoe-horning onto Om Next is gone, so the internals are waaaay simpler to deal with. The transactional semantic stuff is the most complicated, but I’m going to make that optional…it is necessary for bw compat, but not necessary for new apps I don’t think

tony.kay04:05:10

Also nice to know I can get an advanced compile to work on the first try….that was a nice change of pace…usually have to fight that one pretty hard

💯 8
carkh04:05:43

As a first step to building a custom remote, i'm trying to reproduce the "remotes as abstraction" code in my project

carkh04:05:00

and i'm now moving parts of the code in their own namespaces

carkh04:05:35

i get all kind of problems with symbol namespaces

carkh04:05:19

so in my background-remote ns, i have this : (defmethod ls-mutate `set-some-key ...`

carkh04:05:10

hum dunno how to escape the backtick in slack >>

carkh04:05:30

in my page file let's call it namespace root, i have (defmutation set-some-key ...

carkh04:05:02

and in the Root macro defsc onClick #(prim/transact! this `[(set-some-key...

carkh04:05:57

it looks like the defmutation is correctly applying the action

carkh04:05:20

but ut won't call the background-remote, logging an error in the console

carkh04:05:36

No method in multimethod 'cark.tags.options.background-remote/ls-mutate' for dispatch value: cark.tags.ui.options-page/set-some-key

carkh04:05:14

i can make it work by namespacing everything to my background-remote namespace, but that's obviously wrong

carkh04:05:33

so my question is, how do i namespace everything so that it's both modular and working ?

carkh04:05:11

for now i did (defmulti ls-mutate (fn [_ key _] (symbol (name key)))) in background-remote. and (defmethod ls-mutate 'set-some-key ...

hyoo22:05:49

Hi @tony.kay, I am trying to test ident queries, but somehow it does not work as I expected. Here is a sample code:

(defsc Ref1
  [this props]
  {:query [:title]
   :initial-state {:title "some-title"}
   :ident (fn [] [:ref 1])})

(defsc SomeData
  [this props]
  {:query [{[:ref 1] (prim/get-query Ref1)}]
   :initial-state (fn [_] {})}
  ;;;;; props contain {[:ref 1] {}}, not  {[:ref 1] {:title "some-title"}}
  (dom/p (str props)))

(def ui-some-data (prim/factory SomeData))

(defsc Root
  [this props]
  {:query         [{:ref1 (prim/get-query Ref1)}
                   {:some-data-list (prim/get-query SomeData)}]
   :initial-state (fn [_]
                    {:ref1 (prim/get-initial-state Ref1 {})
                     :some-data-list [(prim/get-initial-state SomeData {})]})}
  (map ui-some-data (:some-data props)))

tony.kay22:05:24

you cannot have just a link query in a component

tony.kay22:05:41

well, let me think abt that

tony.kay22:05:49

actually, if you have initial state it prob should work…

hyoo23:05:11

if the init state is not a vec, it works.

tony.kay23:05:27

So, in general link queries as a “way to walk the graph” are not recommended

tony.kay23:05:55

in practice you won’t know that the ID is 1..so this is kind of a contrived example

tony.kay23:05:28

The limitation is that components that query for NOTHING of their own (like SomeData) won’t work right

tony.kay23:05:39

What you want to do is join Ref1 in initial state to SomeData

tony.kay23:05:48

and just make up a kw for the “edge”

tony.kay23:05:27

(defsc SomeData
  [this props]
  {:query [{:edge (prim/get-query Ref1)}]
   :initial-state (fn [_] {:edge (prim/get-initial-state Ref1 {}})}
  ;;;;; props contain {[:ref 1] {}}, not  {[:ref 1] {:title "some-title"}}
  (dom/p (str props)))

tony.kay23:05:52

if Ref1 has a constant ident, that’s fine…this will join it always

tony.kay23:05:56

ident queries in components are highly discouraged. Link queries (where the second element is _) are meant for things like “logged in user” and “load marker table”.

tony.kay23:05:16

ident queries are really meant for talking to servers

tony.kay23:05:37

e.g. I want to load person 1: [{[:person/id 1] (prim/get-query Person)}]

tony.kay23:05:56

which is what (df/load this [:person/id 1] Person) sends

tony.kay23:05:15

in a static component definition, you typically don’t want to embed a static ident. If it really is a singleton, the edge invention above hurts nothing.

tony.kay23:05:53

The one exception that is reasonable is if there is a singleton that you really do need to dynamically get to from various instances of a component, then it’s OK, but you have to be careful that your query contains something besides just the ref join

tony.kay23:05:27

Oh, your code has a typo, too

tony.kay23:05:38

(map ui-some-data (:some-data props))

tony.kay23:05:44

:some-data is not what you called it

tony.kay23:05:51

it might actually work if you fix that

hyoo23:05:57

haha. thanks for your prompt answer. As you just said, I have different kinds of components that refer a singleton component.

tony.kay23:05:02

If you use props destructuring Fulcro will warn you abt that

hyoo23:05:29

I’ll try your suggestion using :edge, but I am curious about :initial-state (fn [_] {:edge (prim/get-initial-state Ref1 {}})}.

tony.kay23:05:43

and :initial-state has a non-lamba version that also error checks (and auto-wraps get-initial-state):

(defsc Root
  [this {:keys [ref1 some-data-list]}]
  {:query         [{:ref1 (prim/get-query Ref1)}
                   {:some-data-list (prim/get-query SomeData)}]
   :initial-state 
                    {:ref1 {}
                     :some-data-list [{}]})}
  (map ui-some-data some-data-list))

tony.kay23:05:02

The maps in the non-lambda initial state are automatically turned into what you wrote by looking at the query

tony.kay23:05:13

even in to-many

tony.kay23:05:20

the maps are the params

hyoo23:05:43

If I have that code, doesn’t it initialize the edge every time when an SomeData is created?

tony.kay23:05:45

and those are also error-checked by the macro

tony.kay23:05:59

initial-state means ON APP start

tony.kay23:05:04

not on every mount

tony.kay23:05:35

Fulcro creates your initial state database by normalizing initial-state using root query

tony.kay23:05:38

that’s all it does

tony.kay23:05:44

gives your app an “initial state”

tony.kay23:05:50

it is not a component constructor

tony.kay23:05:31

(though you can call get-initial-state yourself at any time, if you want, for your own needs)