fulcro

Book: http://book.fulcrologic.com, Community Resources: https://fulcro-community.github.io/, RAD book at http://book.fulcrologic.com/RAD.html
stuartrexking 2021-02-09T00:11:28.291800Z

Is there an idiomatic approach to using third party APIs. If wanted to use the Cognito JS lib for auth from the client, would the best (right) approach be to write a remote implementation as per https://book.fulcrologic.com/#_writing_your_own_remote_implementation?

tony.kay 2021-02-09T02:15:53.292700Z

Depends on how the API works. A lot of auth APIs do redirects and such, and are intended to be embedded as-is and called from js. You could integrate a remote to talk to it, or you could call the methods of it directly.

tony.kay 2021-02-09T02:16:17.292900Z

you have merge-component and mutations you can call to interact with the app, so either direction is really ok.

tony.kay 2021-02-09T02:17:01.293100Z

You’re not the one doing to “talking”, so a remote may or may not make sense…an API that just wants you to give it callbacks are often best just used as designed, and pass them callbacks the trigger mutations via transact!

stuartrexking 2021-02-09T02:20:40.293400Z

That last point makes sense.

stuartrexking 2021-02-09T02:25:24.293600Z

I’m using a lib that wraps all the API stuff, and returns a promise. I’ll use it directly then trigger a mutation.

tony.kay 2021-02-09T04:58:05.301100Z

I’m working on a feature I’ve been wanting to add for roughly 3 years, but never get around to: batched networking. The idea is simple: When multiple loads are enqueued while you hold the thread, combine them into one network request. The implementation is a bit harder, because of numerous semantic guarantees that webapps want (and often don’t have), and that Fulcro provides. I’ve got an experimental version that I’ve released as 3.4.17-SNAPSHOT on clojars. DO NOT USE THIS VERSION IN PRODUCTION. It has debug logging messages, has not been well-tested, and defaults to allowing batch (which I want to make an option), I’m asking anyone that is interested in this feature to try this version out on their apps and let me know if it works. It will console.log a bunch of crap in js. Instructions: 1. You must be using handle-api-request from Fulcro on the server-side. The new handler understands how I’m combining requests. 2. I have not tried any extended http remote middleware. It should work, but beware. 3. Run some part of your app that issues a bunch of loads from a single thread of execution (e.g. startup that loads config, etc.) 4. Look at the normal Network tab in js dev tools. You should see those loads combined into one network request. 5. Look in Inspect. You should still see each load listed separately in the Network tab there. The times will be a little misleading, since they happened in parallel. You’ll see that each took a slight bit longer than the other (that’s the internal processing time diff). NOTE: Mutations are NOT batched. Only loads that are queued together while holding the thread, and which do NOT have an abort ID. Requests with abort IDs have to be sent by themselves or you cannot abort them alone 🙂

4😻
tony.kay 2021-02-09T04:59:33.301800Z

I’ve tried it on a simple app so far, but have been working for about 13 hours…so I’m done for now.

1😴
tony.kay 2021-02-09T21:58:04.315900Z

I ran this in a large production app, and it seems to be ok. I saw one potential issue, but I could never figure out if it was this change, or just a bug in the prod app. I’ll continue down the path of making this a thing you can enable, and hopefully get it on an official release soon.

aratare 2021-02-09T09:50:55.306700Z

Hi there. Is there a way to reset the app state? To elaborate, I want to reset the app state after logging out so it looks exactly like when the user opens the app for the first time. Thanks in advance.

roklenarcic 2021-02-09T11:01:06.307600Z

Has anyone tried to plug some non-datomic datalog DB to Fulcro? Like Crux, Datalevin or Datahike?

henrik 2021-02-09T11:17:51.307700Z

Yep, it's mainly a performance optimization thing. There's a big gain in only refreshing the input itself (both with :only-refresh and :synchronous? true). Unfortunately, there are some parts of the parent that does need refreshing, but far from all of it. I was hoping that I could be more surgical when issuing refreshes.

henrik 2021-02-09T11:18:09.307900Z

This holds true for prod.

henrik 2021-02-09T11:24:02.308100Z

> Why do you care which inputs “refresh”. You’re in control of their state, so the question of React checking them should be irrelevant. You control the state. Do what is right in state, and trust the UI to be correct. I care because it involves recalculating some pretty intense computed props when I refresh the parent wholesale. I.e., bits of the parent need to update on input, bits only need to update between interactions with input. The prior are cheap, and the latter are expensive.

henrik 2021-02-09T11:25:37.308300Z

> Targeted refresh requires each input have an ident, which means making quite the mess of your UI tree to get what you’re talking about. Typically a form will have an ident, and that is what you can control refreshing.  If you rewrite so that inputs have idents, it means you spread your form out in a really wacky way, and have to invent idents on the fly (and not use form state). As it happens, our inputs (or rather, the area "just around" each input) do have natural idents, so we can target them very finely. This is the ident that transact!! ends up targeting. It never gets to the form itself, since there's a natural ident on the way there. The form also has a natural ident.

henrik 2021-02-09T11:27:06.308500Z

Form state has turned out to work fine in this scenario! Unfortunately, we might need to opt out of it perhaps, which would be a bummer.

Björn Ebbinghaus 2021-02-09T11:47:51.309100Z

Are you talking about Fulcro RAD? Because Fulcro itself doesn't care about your backend very much.

roklenarcic 2021-02-09T12:24:30.309300Z

yeah about fulcro RAD

henrik 2021-02-09T12:34:57.309500Z

This sounds highly domain-specific to me, and it's hard to give any advice beyond explaining how to manipulate data generally. 1. You can swap! on the state in a mutation action, and clear out whatever you want. But maybe you want to, 2. Load some fresh data from the server on log out, in which case the mutation could return that on remote.

aratare 2021-02-09T13:56:00.309700Z

Will try. Thanks 🙂

aratare 2021-02-09T14:01:12.312800Z

Hi there again. I'm stumbling across some components not rendering like I want them to but not sure how or where I screwed up. So essentially I have a Main component

(defsc Main
  [this {:ui/keys [is-loading?] :as props}]
  {:ident         (fn [] [:component/id :main])
   :query         [:ui/is-loading?]
   :initial-state {:ui/is-loading? false}
   :route-segment ["main"]
   :will-enter    (fn [_ _] (route-immediate [:component/id :main]))}
  (let [app-state (current-state app)
        tab-query [{:session/current-user [{:user/tabs (comp/get-query Tab)}]}]
        user-tabs (db->tree tab-query app-state app-state)]
    (dom/div
      (ui-tab-list (:session/current-user user-tabs)))))

(def ui-main (comp/factory Main))
and a TabList component
(defsc TabList
  [this {:keys [user/tabs ui/selected-tab]}]
  {:query         [:ui/selected-tab :user/tabs]
   :ident         (fn [] [:component/id :tab-list])}
  (let [tabs               (mapv #(assoc % :ui/selected (= (:tab/id %) selected-tab)) tabs)
        on-select-callback (fn [id])]
    (dom/div
      (eui/ui-button {:onClick #(m/set-string! this :ui/selected-tab :value 2)} "Test")
      (eui/ui-button nil (or selected-tab "Hello"))))

(def ui-tab-list (comp/factory TabList))
But whenever I press the Test button it seems that the client db is updated with the new value 2 but the component still displays the old value. Would appreciate any insight. Thanks in advance 🙂

Jakub Holý 2021-02-09T15:05:56.312900Z

Yes. You can save a copy of the app state before your login mutation changes it and then restore it via swap! in the logout mutation.

Jakub Holý 2021-02-09T15:08:21.313100Z

you should not rad app state yourself, let Fulcro do it for you. Make sure that queries are composed correctly. See https://github.com/fulcro-community/guides/blob/main/minimalist-fulcro-tutorial/index.adoc#components-query and https://blog.jakubholy.net/2020/troubleshooting-fulcro/

Jakub Holý 2021-02-09T15:09:52.313500Z

i.e. Main query should most certainly include (comp/get-query TabList) and TabList should query for what it needs. Equally, TabList should include the Tab query.

aratare 2021-02-09T15:11:48.313800Z

Thanks @holyjak 🙂 I was beginning to suspect it has something to do with how I'm setting up and composing these components. Will give it another go now.

aratare 2021-02-09T15:19:54.314Z

To add a bit more context: the reason why I went down this manual way is because I wanted to to query for :session/current-user (which sits at root) while Main sits at [:component/id :main]. As far as I know there is no other way to query for root-level stuff inside query block of a non-root component because it's relative rather than absolute. Perhaps I've made a fatal mistake and you can indeed do that?

Jakub Holý 2021-02-09T17:23:43.314300Z

there is - https://book.fulcrologic.com/#_link_queries

aratare 2021-02-09T17:27:51.314500Z

That is quite life-changing! Or it's just me not reading things properly 😅 Either way thanks a lot 🙂

1👍
tony.kay 2021-02-09T21:58:04.315900Z

I ran this in a large production app, and it seems to be ok. I saw one potential issue, but I could never figure out if it was this change, or just a bug in the prod app. I’ll continue down the path of making this a thing you can enable, and hopefully get it on an official release soon.

tony.kay 2021-02-09T22:00:09.316100Z

No, but if you read the source of the datomic adapter, you’ll see that adapting to something like Crux is nearly trivial.

1👍
tony.kay 2021-02-09T22:07:27.316300Z

So, in answer to your original question: the !! variants are about trying to get a synchronous data result on the current thread (without async submission, which is the default for Fulcro). This is an extreme optimization that you usually don’t need. See https://www.youtube.com/watch?v=102nHLzIbE4&list=PLVi9lDx-4C_TBRiHfjnjXaK2J3BIUDPnf&index=2&ab_channel=TonyKay for the overall motivation (not needing wrapped inputs for React inputs). So, in terms of your actual base need of only refreshing a couple of things, the !! variant happens to also only bother refreshing the current component, which it assumes contains the input you’re trying to “fix”. The :only-refresh option is designed for your particular use-case, but I honestly don’t remember if the !! variant will even honor it.

1👍
tony.kay 2021-02-09T22:08:24.316600Z

As a secondary comment on performance: Have you considered reifying (into app state) or memoizing the “expensive” parent computations?