Skip to content

Commit f8af48f

Browse files
zeitsteinrschmukler
authored andcommitted
docs: add docstrings for most commonly used APIs
Identical content to thheller#2, just cleans up the commit log. All credit goes to @zeitstein
1 parent 12d95da commit f8af48f

File tree

6 files changed

+429
-28
lines changed

6 files changed

+429
-28
lines changed

src/main/shadow/grove.cljs

Lines changed: 200 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,35 @@
2424

2525
(set! *warn-on-infer* false)
2626

27-
(defn dispatch-up! [{::comp/keys [^not-native parent] :as env} ev-map]
27+
(defn dispatch-up!
28+
"Use within a component event handler to propagate the event `ev-map` up the
29+
component tree. `env` is the environment map available in event handlers."
30+
[{::comp/keys [^not-native parent] :as env} ev-map]
2831
{:pre [(map? env)
2932
(map? ev-map)
3033
(qualified-keyword? (:e ev-map))]}
3134
;; FIXME: should schedule properly when it isn't in event handler already
3235
(gp/handle-event! parent ev-map nil env))
3336

3437
(defn query-ident
38+
"Queries the db starting from `ident`.
39+
* `query` - Optional. EQL query. Defaults to ident lookup if not provided.
40+
* `config` - Optional. Any kind of config that may come up.
41+
Changes to idents accessed by the query (including inside `eql/attr`) during
42+
transactions will cause the query to re-un.
43+
---
44+
Example
45+
```clojure
46+
(defmethod eql/attr ::contains
47+
[env db {:dir/keys [files dirs] :as current} query-part params]
48+
(cond->> (concat dirs files)
49+
(not (::show-hidden? db))
50+
(filterv (fn [ident] (not (::hidden? (get db ident)))))))
51+
(bind {:as query-result
52+
:dir/keys [name open?]
53+
::keys [contains]}
54+
(sg/query-ident ident [:dir/name :dir/open? ::contains]))
55+
```"
3556
;; shortcut for ident lookups that can skip EQL queries
3657
([ident]
3758
(impl/hook-query ident nil {}))
@@ -42,16 +63,45 @@
4263
(impl/hook-query ident query config)))
4364

4465
(defn query-root
66+
"Queries from the root of the db.
67+
* `query` - EQL query.
68+
* `config` - Optional. Any kind of config that may come up.
69+
Changes to idents accessed by the query (including inside `eql/attr`) during
70+
transactions will cause the query to re-un.
71+
---
72+
Example
73+
```clojure
74+
(defmethod eql/attr :products-in-stock [env db _ _]
75+
(->> (db/all-of :product)
76+
(filter #(pos? (:stock %)))
77+
(mapv :db/ident)))
78+
(defc ui-homepage []
79+
(bind {:keys [products-in-stock a-root-key]}
80+
(sg/query-root [:products-in-stock :a-root-key]))
81+
```"
4582
([query]
4683
(impl/hook-query nil query {}))
4784
([query config]
4885
(impl/hook-query nil query config)))
4986

5087
(defn run-tx
88+
"Use inside a component event handler. Runs transaction `tx`, e.g.
89+
`{:e ::some-event :data ...}`. `env` is the component environment map
90+
available in event handlers.
91+
---
92+
Example
93+
```clojure
94+
(event :hide! [env ev-map event]
95+
(when (.-ctrlKey event)
96+
(sg/run-tx env ev-map)))
97+
```"
5198
[{::rt/keys [runtime-ref] :as env} tx]
5299
(impl/process-event runtime-ref tx env))
53100

54-
(defn run-tx! [runtime-ref tx]
101+
(defn run-tx!
102+
"Runs the transaction `tx`, e.g. `{:e ::some-event :data ...}`, outside of the
103+
component context."
104+
[runtime-ref tx]
55105
(assert (rt/ref? runtime-ref) "expected runtime ref?")
56106
(let [{::rt/keys [scheduler]} @runtime-ref]
57107
(gp/run-now! scheduler #(impl/process-event runtime-ref tx nil) ::run-tx!)))
@@ -63,11 +113,23 @@
63113
(js-delete root-el "sg$env")))
64114

65115
(defn watch
66-
"hook that watches an atom and triggers an update on change
67-
accepts an optional path-or-fn arg that can be used for quick diffs
116+
"Hook that watches `the-atom` and updates when the atom's value changes.
68117
118+
Accepts an optional `path-or-fn` arg that can be used to 'watch' a portion of
119+
`the-atom`, enabling quick diffs.
120+
* 'path' – as in `(get-in @the-atom path)`
121+
* 'fn' - similar to above, defines how to access the relevant parts of
122+
`the-atom`. Takes [old-state new-state] of `the-atom` and returns the actual
123+
value stored in the hook. Example: `(fn [_ new] (get-in new [:id :name]))`.
124+
125+
**Use strongly discouraged** in favor of the normalized central db.
126+
127+
---
128+
Examples
129+
```clojure
69130
(watch the-atom [:foo])
70-
(watch the-atom (fn [old new] ...))"
131+
(watch the-atom (fn [old new] ...))
132+
```"
71133
([the-atom]
72134
(watch the-atom (fn [old new] new)))
73135
([the-atom path-or-fn]
@@ -76,6 +138,7 @@
76138
(atoms/AtomWatch. the-atom path-or-fn nil nil))))
77139

78140
(defn env-watch
141+
"Similar to [[watch]], but for atoms inside the component env."
79142
([key-to-atom]
80143
(env-watch key-to-atom [] nil))
81144
([key-to-atom path]
@@ -85,13 +148,46 @@
85148
(vector? path)]}
86149
(atoms/EnvWatch. key-to-atom path default nil nil nil)))
87150

88-
(defn suspense [opts vnode]
151+
(defn suspense
152+
"See [docs](https://github.com/thheller/shadow-experiments/blob/master/doc/async.md)."
153+
[opts vnode]
89154
(suspense/SuspenseInit. opts vnode))
90155

91-
(defn simple-seq [coll render-fn]
156+
(defn simple-seq
157+
"Creates a collection of DOM elements by applying `render-fn` to each item
158+
in `coll`. `render-fn` can be a function or component.
159+
160+
Makes no attempts to minimize DOM operations required for updates. Efficient
161+
with colls which change infrequently or colls updated at the tail. Otherwise,
162+
consider using [[keyed-seq]].
163+
---
164+
Example:
165+
```clojure
166+
(sg/simple-seq
167+
(range 5)
168+
(fn [num]
169+
(<< [:div \"inline-item: \" num])))
170+
```"
171+
[coll render-fn]
92172
(sc/simple-seq coll render-fn))
93173

94-
(defn keyed-seq [coll key-fn render-fn]
174+
(defn keyed-seq
175+
"Creates a keyed collection of DOM elements by applying `render-fn` to each
176+
item in `coll`.
177+
* `key-fn` is used to extract a unique key from items in `coll`.
178+
* `render-fn` can be a function or component.
179+
Uses the key to minimize DOM updates. Consider using instead of (the more
180+
lightweight) [[simple-seq]] when `coll` changes frequently.
181+
182+
---
183+
Examples:
184+
```clojure
185+
;; ident used as key
186+
(keyed-seq [[::ident 1] ...] identity component)
187+
(keyed-seq [{:id 1 :data ...} ...] :id
188+
(fn [item] (<< [:div.id (:data item) ...])))
189+
```"
190+
[coll key-fn render-fn]
95191
(sc/keyed-seq coll key-fn render-fn))
96192

97193
(deftype TrackChange
@@ -132,20 +228,25 @@
132228
(volatile! nil))
133229

134230
(defn effect
135-
"calls (callback env) after render when provided deps argument changes
136-
callback can return a function which will be called if cleanup is required"
231+
"Calls `(callback env)` after render when `deps` changes. (*Note*: will be
232+
called on mount too.) `callback` may return a cleanup function which is
233+
called on component unmount *and* just before whenever callback would be
234+
called."
137235
[deps callback]
138236
{:pre [(fn? callback)]}
139237
(comp/EffectHook. deps callback nil true nil))
140238

141239
(defn render-effect
142-
"call (callback env) after every render"
240+
"Calls `(callback env)` after every render. `callback` may return a cleanup
241+
function which is called on component unmount *and* after each render before
242+
callback."
143243
[callback]
144244
{:pre [(fn? callback)]}
145245
(comp/EffectHook. :render callback nil true nil))
146246

147247
(defn mount-effect
148-
"call (callback env) on mount once"
248+
"Calls `(callback env)` on mount. `callback` may return a cleanup function
249+
which is called on unmount."
149250
[callback]
150251
{:pre [(fn? callback)]}
151252
(comp/EffectHook. :mount callback nil true nil))
@@ -220,7 +321,12 @@
220321
(set! (.-sg$env root-el) new-env)
221322
::started)))
222323

223-
(defn render [rt-ref ^js root-el root-node]
324+
(defn render
325+
"Renders the UI root. Call on init and `^:dev/after-load`.
326+
* `rt-ref` – runtime atom
327+
* `root-el` – DOM element, e.g. `(js/document.getElementById \"app\")`.
328+
* `root-node` – root fn/component (e.g. defined with `defc`)."
329+
[rt-ref ^js root-el root-node]
224330
{:pre [(rt/ref? rt-ref)]}
225331
(gp/run-now! ^not-native (::rt/scheduler @rt-ref) #(render* rt-ref root-el root-node) ::render))
226332

@@ -320,6 +426,19 @@
320426
(goog-define TRACE false)
321427

322428
(defn prepare
429+
"Initialises the runtime atom.
430+
* `init` – Optional. A map.
431+
* `data-ref` – Ref to the grove db atom.
432+
* `runtime-id`
433+
434+
---
435+
Example:
436+
437+
```clojure
438+
(defonce rt-ref
439+
(-> {::rt/tx-reporter (fn [report] (tap> report))}
440+
(rt/prepare data-ref ::my-rt)))
441+
```"
323442
([data-ref runtime-id]
324443
(prepare {} data-ref runtime-id))
325444
([init data-ref runtime-id]
@@ -357,15 +476,80 @@
357476
(defn vec-conj [x y]
358477
(if (nil? x) [y] (conj x y)))
359478

360-
(defn queue-fx [env fx-id fx-val]
479+
(defn queue-fx
480+
"Used inside an event handler, it queues up the registered handler of `fx-id`
481+
to run at the end of the transaction. The handler will be called with
482+
`fx-val`.
483+
---
484+
Example:
485+
```clojure
486+
(sg/reg-event rt-ref ::toggle-show-hidden!
487+
(fn [tx-env {:keys [show?] :as event}]
488+
(-> tx-env
489+
(assoc-in [:db ::show-hidden?] show?) ;; modify the db
490+
(sg/queue-fx ::alert! event)))) ;; schedule an fx
491+
(sg/reg-fx rt-ref ::alert!
492+
(fn [fx-env {:keys [show?] :as fx-val}]
493+
(js/alert (str \"Will \" (when-not show? \"not\") \" show hidden files.\"))))
494+
```"
495+
[env fx-id fx-val]
361496
(update env ::rt/fx vec-conj [fx-id fx-val]))
362497

363-
(defn reg-event [rt-ref ev-id handler-fn]
498+
(defn reg-event
499+
"Registers the `handler-fn` for event `ev-id`. `handler-fn` will be called
500+
with `{:as tx-env :keys [db]} event-map` and should return the modified
501+
`tx-env`.
502+
503+
There is an alternative approach to registering event handlers, see examples.
504+
505+
---
506+
Example:
507+
```clojure
508+
(sg/reg-event rt-ref ::complete!
509+
(fn [tx-env {:keys [checked ident] :as ev}]
510+
(assoc-in tx-env [:db ident :completed?] checked)))
511+
512+
;; metadata approach
513+
(defn complete! {::ev/handle ::complete!}
514+
[tx-env {:keys [checked ident] :as ev}]
515+
(assoc-in tx-env [:db ident :completed?] checked))
516+
517+
;; use `{:dev/always true}` in namespaces utilising the metadata approach.
518+
```"
519+
[rt-ref ev-id handler-fn]
520+
364521
{:pre [(keyword? ev-id)
365522
(ifn? handler-fn)]}
366523
(swap! rt-ref assoc-in [::rt/event-config ev-id] handler-fn)
367524
rt-ref)
368525

369-
(defn reg-fx [rt-ref fx-id handler-fn]
526+
(defn reg-fx
527+
"Registers the `handler-fn` for fx `fx-id`. fx is used for side effects, so
528+
`handler-fn` shouldn't modify the db.
529+
530+
---
531+
Examples:
532+
```clojure
533+
(sg/reg-event rt-ref ::toggle-show-hidden!
534+
(fn [tx-env {:keys [show?] :as event}]
535+
(-> tx-env
536+
(assoc-in [:db ::show-hidden?] show?) ;; modify the db
537+
(sg/queue-fx ::alert! event)))) ;; schedule an fx
538+
539+
(sg/reg-fx rt-ref ::alert!
540+
(fn [fx-env {:keys [show?] :as fx-data}]
541+
(js/alert (str \"Will \" (when-not show? \"not\") \" show hidden files.\"))))
542+
```
543+
544+
`(:transact! fx-env)` allows fx to schedule another transaction, but it
545+
should be an async call:
546+
```clojure
547+
(sg/reg-fx rt-ref :ui/redirect!
548+
(fn [{:keys [transact!] :as env} {:keys [token title]}]
549+
(let [tokens (str/split (subs token 1) #\"/\")]
550+
;; forcing the transaction to be async
551+
(js/setTimeout #(transact! {:e :ui/route! :token token :tokens tokens}) 0))))
552+
```"
553+
[rt-ref fx-id handler-fn]
370554
(swap! rt-ref assoc-in [::rt/fx-config fx-id] handler-fn)
371555
rt-ref)

0 commit comments

Comments
 (0)