Skip to content

Instantly share code, notes, and snippets.

@holyjak
Last active March 3, 2021 01:11
Show Gist options
  • Save holyjak/5bad02dc378fe57b3111edab42b4adbc to your computer and use it in GitHub Desktop.
Save holyjak/5bad02dc378fe57b3111edab42b4adbc to your computer and use it in GitHub Desktop.
WIP Fulcro Gotchas document

Common misconceptions and pitfalls in Fulcro.

General

You query the client DB, not the server!

A common misconception is that the Root’s :query is used to load! the app data, i.e. that something like Root query → load! → Root props → rendered UI is happening. It is not. What is happening is Root query → client DB → Root props → rendered UI. You can use a query to also load some data from the backend to feed the client DB but this is up to you, has nothing to do with the just described cycle, and does not need to happen. Also, you essentially never load! the Root query. Instead, you load data for distinct UI subtrees, i.e. sub-queries. So these are two orthogonal, independent processes: rendering client data into the UI and feeding the client database.

A component’s query is relative to its parent component, only Root can "see" keys at the root of the client DB*

(* That is unless the componet uses a Link or Ident query.)

This will not show any data:

;; Given this client DB:
{:project/all-projects [{:project/name "Project 1"}]
 :component/id  {:Projects {}}
 :root/projects [:component/id :Projects]}

(defsc Projects [_ {all-projects :project/all-projects}]
  {:query [:project/all-projects]
   :ident (fn [] [:component/id :Projects])
   :initial-state {}}
  (div
    (h3 "My Projects")
    (pr-str all-projects)))

(defsc Root [_ {projects :root/projects}]
  {:query [{:root/projects (comp/get-query Projects)]
   :initial-state {:root/projects {}}}
  (ui-projects projects))

If you look at root query:

Root query
(comp/get-query Root)
;=> [{:root/projects [:project/all-projects]}]

and the de-normalized client DB (where the [:component/id :Projects] ident/"pointer" was replaced with the value):

De-normalized client DB
{:project/all-projects [{:project/name "Project 1"}]
 :root/projects {}}

you will se why. We are asking essentially for (→ denormalized-db :root/projects :project/all-projects) but there is no such thing.

The query of a child (here Projects) is relative to its parent (here Root) so the keys used in the query (:project/all-projects) need to be in the client DB under the parent’s ident. Only the Root component itself can see and ask for keys directly at the root of the client DB. That is why correctly "targeting" data, i.e. adding "edges" or "pointers" from the parent component to the loaded data, is critical.

How to fix? We made a mistake when loading the data. We should not have put them in the root but should have targeted them to the Projects component by modifying the load thus: (df/load! :project/all-projects Projects {:target (targeting/replace-at [:component/id :Projects :project/all-projects])}).

The client DB isn’t exclusively for normalized component data - you can put there anything!

You might have got the impression that the only thing that goes into the client DB is the normalized data of Fulcro compontents, coming from initial data and load!-ed from the backend. While this data certainly is there, you can also put there any additional data you want, in whatever form you want. The client DB is just a map and you can assoc anything into it, e.g. in a mutation. It is perfectly fine to make up you own top-level keys and put there things like :current-user-name "linus" and :preferred beverages ["beer" "beer" "beer"].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment