Fork me on GitHub
#fulcro
<
2021-04-05
>
dvingo01:04:11

It's the global-eql-transform property of a fulcro app. I ran into this as well, was seeing mutations sending form config and other superfluous things to the server, I eventually implemented my own elision logic, but then later learned the fulcro rad stuff did the same thing: https://github.com/fulcrologic/fulcro-rad/blob/528bc320888c71e410c623e1ce2efb2e08b821a7/src/main/com/fulcrologic/rad/application.cljc#L92 It should really be the default for fulcro apps in general

metal 6
dvingo01:04:13

that one also doesn't remove link queries though

tony.kay01:04:28

For those following along on the development of “raw” Fulcro, I’ve made a new commit at SHA cb6c90fedb14238f3718a4669c5583013c7605df with some cleanup and refactoring. There is now com.fulcrologic.fulcro.alpha.raw which contains functions for adding a root or UISM to Fulcro with no explicit use of React. The raw-components3 namespace was rewritten to leverage raw via React hooks, and a demo in react is in https://github.com/fulcrologic/fulcro/blob/feature/fulcro-3.5/src/workspaces/com/fulcrologic/fulcro/cards/composition4_cards.cljs#L330 I refined that UISM card some so that it works better (it assume you already had a session, so you can run through all of the real states). The main improvement here is that those of you wanting to play with Fulcro outside of the confines of React can now more easily do so. The library will still look for React in a CLJS context, of course, but using the raw ns allows you to just add functions that can receive prop trees as callbacks that run when Fulcro transaction steps complete (optimistic tx, return from remote, etc.) I think perhaps @smith.adriane and @thheller were looking to make use of this particular approach, and I’m interested to see what else the community might come up with. I’m not releasing a formal version yet, so use the git sha w/deps if you want to play with it.

🎉 24
💯 3
phronmophobic05:04:09

neat! I'll check it out!

thheller11:04:16

there are however still a whole lot of react references everywhere so running it without react doesn't work. it does no rendering with react but form-state breaks if react isn't loaded

thheller11:04:09

(bind {:ui/keys [saving?]
         :user/keys [id name settings] :as u}
    (use-root :current-user User {}))

thheller11:04:05

thats basically the emulated raw3/use-root. I don't know if use-fulcro is actually needed anywhere when none of the react-factory or DOM things are used

tony.kay14:04:21

I think that’s right @thheller. I have not had time to review the dynamic vars completely, but the with-fulcro thing may not actually be necessary in most/all cases. It’s going to take a little more refactoring to get React completely out of code paths. Dynamic routing is broken because it expects there to be an app root, and it sounds like form-state as well, though both should be fixable I’d think.

tony.kay15:04:45

Looking through it, it’s kind of a big project to isolate React as a library without breaking existing programs. I don’t think it’s terribly difficult, just a lot of tedious code movement of non-react stuff to new nses, and then making aliases from the original spots to them so that nses like form-state and such can refer to the common non-react stuff instead of the “mix” that is components currently.

tony.kay15:04:57

Same for application. 99% React-free, but it has a tiny bit mixed in.

thheller15:04:15

yeah I can imagine. getting the separation right is difficult

tony.kay18:04:51

It isn’t too terrible actually. I think I’ve got most of it done (sans testing).

tony.kay22:04:47

SHA c5a0cfe60c281c4a25f7de966298b67a349fccab has a first pass that should do it for most nses. There is now a raw.application and raw.components ns that are the base for the legacy ones. I also put in some fixes related to using this all from Clojure. So, to try it out and avoid react completely, use raw.components and raw.application nses.

tony.kay22:04:45

alpha.raw pulls in both of those, and has use-root!

tony.kay22:04:04

oh…missed something..sb

tony.kay22:04:32

ad2425b9973af287f6b64fe9ad56808dd8c69eaa is the better SHA

tony.kay22:04:45

the nc moved to raw.components, and formc to form-state

tony.kay22:04:14

I have not tested this against live UI much, but all tests pass

zhuxun205:04:03

Is there a way to store something on the action section of a mutation, and retrieve it on the ok-action section? I wanted to implement state recovery when the server responds a failure.

Jakub Holý (HolyJak)10:04:10

Not sure what is optimal but you can always use the state (ie client DB)...

dvingo13:04:39

I have used the runtime atom for that - communicating to the error-action in this case the prior state of the app if a network call fails in order to rollback to the prior app state.

(defmutation delete-goal
  [{:keys [goal-id]}]
  (action [{:keys [state]}]
    (let [goal           (get-goal-tree goal-id @state)
          all-goals-flat (goal-tree->vec goal)]
      (swap! state
        (fn [s]
          (reduce (fn [acc goal] (ns/remove-entity acc (dm/goal-ident goal))) s all-goals-flat)))))

  (remote [{:keys [app state-before-action]}]
    (let [runtime-atom (get app ::app/runtime-atom)]
      (swap! runtime-atom (fn [s] (assoc s :prior-state state-before-action))))
    true)

  (error-action [{:keys [app state result]}]
    (let [runtime-atom (get app ::app/runtime-atom)
          {:keys [prior-state]} @runtime-atom
          server-error (fu/get-server-mutation-err result)]
      (let [new-state
            (-> prior-state
              (assoc-in (dm/goal-ident goal-id :ui/submit-state) :state/failed)
              (assoc-in (dm/goal-ident goal-id :ui/server-message) server-error))]
        (reset! state new-state)))))

tony.kay15:04:12

Technically the most appropriate answer is what @U0522TWDA said: I would consider that “state”, and state atom is what I’d typically use. See form-state, for example: the entire basic mechanism of that is to make a copy of something (pristine) and then let you update it and try to save it. Ultimately you can either choose to revert it (copy the pristine back over the original) or commit the changes. Another thing to consider is that the params to the mutation are available in every section of a mutation, and do not change. You can close over whatever you want/need there, which allows you to control “state capture” from the UI. The runtime-atom is also a completely legitimate solution, particularly if what you want is more time-travel of state. That’s a lot harder to make user-friendly, because user’s don’t expect things to go back in time at some future point of interaction. A slow network interaction could allow them to move on to a whole other are of the app, which you then “snap them” back from.

dvingo18:04:36

has anyone tried using guardrails with malli?

Tyler Nisonoff20:04:14

Suppose I have a screen containing Entities A and B that looks like this:

-----------------------
|       A              |
|     =======          |
|    |   B  |          |
|    |======|          |
|       A              |
|    |======|          |
|    |  B   |          |
|.   |======|          |
------------------------
B is naturally a child of A, but when I’m creating Fulcro components of this nature, I find myself needing to create a “meta/union” component for B that encompasses the union of the query for all of the sub-views of B i want displayed, and then passing that prop to various view-specific functions / components that display just a sub-view of B in particular. The query would look like:
{
 :query [:component/id  {:component/subComp (comp/get-query (ui-union-B))}]
 :ident [:component/id :A]
}
Is there a better way for A to query for the individual views of B rather than using the union-component approach, or is that the best strategy? EDIT: maybe https://blog.jakubholy.net/2020/fulcro-divergent-ui-data/#_a_data_entity_spread_across_multiple_sibling_components are what I want in this case?

Tyler Nisonoff19:04:33

@U0522TWDA have you used pathom placeholders with subforms? It seems that add-form-config isn’t finding subforms if my subform is split into placeholders…and struggling to figure out how to get around it

Jakub Holý (HolyJak)19:04:54

No I haven't :( Please let me know if you figure it out!

Jakub Holý (HolyJak)19:04:53

Perhaps subforms require that the thing being edited is an entity of its own and not a part of a bigger entity?

Tyler Nisonoff19:04:44

i think i’ll have to make a duplicate edge from A to B that doesn’t use pathom placeholders, seems the form state code assumes that any join is going to be flat, it doesn’t know how to walk the placeholder map

Tyler Nisonoff20:04:04

^ Pathom placeholders solved this for me! But I’ll leave the comment above in case its useful to others

❤️ 3
cjsauer22:04:29

clj-kondo linting support for fulcro is coming along nicely! I’ve written a custom hook that can understand fulcro’s defmutation macro, as well as simple support for guardrail’s >defn. Both will give you some simple error checking at lint time, as seen in the screenshots. PR for kondo’s shared config repo is here: https://github.com/clj-kondo/config/pull/12 If you’re keen to try out the bleeding edge, my fork is here: https://github.com/cjsauer/config/tree/fulcro-config

fulcro 12
cjsauer12:04:47

Oh nice! I tried searching for prior art and didn’t find this. However the link to the gist is broken for me. It also appears that a PR was never created. @UCFG3SDFV were you still working on this?

tvaughan12:04:57

Here's the version I have:

$ cat .clj-kondo/hooks/fulcro.clj
(ns hooks.fulcro
  ""
  {:author "Adam Helins"}
  (:require
    [clj-kondo.hooks-api :as hook]))

(defn defmutation
  [{:keys [node]}]
  (let [[_call
         sym
         & arg+]    (:children node)
        docstring   (first arg+)
        [[param+
          & fn-like+]
         docstring-2] (if (hook/string-node? docstring)
                        [(rest arg+)
                         docstring]
                        [arg+
                         nil])]
    {:node (hook/list-node (concat [(hook/token-node 'defn)
                                    sym]
                                   (when docstring-2
                                     [docstring-2])
                                   [param+
                                    (hook/vector-node (map #(let [[_sym
                                                                   arg+
                                                                   & body] (:children %)]
                                                              (hook/list-node (list* (hook/token-node 'fn)
                                                                                     arg+
                                                                                     body)))
                                                           fn-like+))]))}))

cjsauer12:04:17

Thanks. This is a simpler approach I think. I was under the incorrect assumption that mutation handlers could refer to each other, but that’s not the case (the :dispatch key in env holds those lambdas). On the flip side, I could see kondo implementing “unused lambda” lints one day, which would throw false positives with the above.

Adam Helins12:04:59

@U6GFE9HS7 Hi, here is my original gist: https://gist.github.com/helins/52d03847157b0dc95c6987844a74dd68 The PR slept out of my mind (had to work fast at that time) and I see you have already proposed something. Let me know it goes through, if not we can use that gist 🙂

cjsauer13:04:01

Thanks, will do :)

Michael Rispoli23:04:48

I was just wondering if anyone had any recommendations for a backend for use with fulcro preferably as a mono repo of some flavor. Just evaluating it for a project and was wondering how the backend data store pairs with the Frontend data store snd what kind of lift that ends up being.

cjsauer23:04:13

I’d say regardless of backend data storage choice, the connective tissue with the client is always the same in Fulcro: pathom resolvers. So it’s pretty backend agnostic in that regard. Going with something you’re familiar with is likely a good choice. The problem domain might lend itself to one database over another as well.

3
cjsauer23:04:16

Fulcro RAD has helpful database adapters for Datomic, and I think SQL (not sure about the state of that at this moment), and just days ago there was a post for a Crux adapter.

3
cjsauer23:04:41

There’s info for using it with each database in the read me.

Michael Rispoli01:04:10

Ah awesome thank you for this I was wondering if Datomic was the right pairing I’ll give this a look!

dvingo11:04:42

I can highly recommend Crux (https://opencrux.com) for use with fulcro - it would fit for similar use cases as Datomic, but is open source and the team is amazingly nice and responsive if you have any feedback or questions

thosmos17:04:02

I’ve only used Fulcro with Datomic, so I’m biased. Fulcro’s query syntax was inspired by Datomic’s pull syntax and was originally designed to work directly with it. Pathom made it much easier to integrate with other DBs, but the pairing is still optimal.

Michael Rispoli23:04:21

Thank you both, Crux looks pretty awesome in general I’ve never heard of it. I’m also wondering does anyone have a good full stack example of fulcro with a backend or template they use. I read the docs but definitely still feel really daunted getting started and would love to poke around with a good starting off point. I’m also new to clojure generally so if fulcro is maybe a tough starting point let me know as well. It just seems like the best option for building a production app though as well.

dvingo00:04:06

I have a full-stack app template I base any new projects off of: https://github.com/dvingo/dv.fulcro-template fulcro may be a tough choice to start with, but as long as your expectations are accurate in terms of learning rate (I have a heuristic of giving myself about 3 months when learning something significantly new) then you should be okay - it would take a similar amount of time no matter what you pick, as you'll have to do the research and integration of a myriad of libraries which become your problem if you don't go with fulcro - in my opinion.

dvingo01:04:09

The reasons I like crux over datomic: • setup. start a new node: (crux/start-node {}) ◦ scales from one node to many seamlessly • no schema • open source and gratis • amazing team • has a query planner so you don't have to ever think about the order of your datalog clauses again • documents over entity attr value triples

Michael Rispoli02:04:24

I like this, I think I’m going to give crux a go regardless now seems really awesome

Michael Rispoli02:04:30

I think the learning curve sounds about right. My team and I are coming from react js/ts backgrounds so I was looking to fulcro because like you said we’d end up trying to build something similar out of libraries and such anyway. Learning curve of a few months seems accurate too, I think we may want to stick to a few internal projects with looser timelines to learn a bit more.

Michael Rispoli02:04:30

We did take up elm fir a few projects and sorta learned as we went and it was fine but this seems like there is a bit higher of a learning curve.