Merge pull request #392 from RobinNagpal/feature/docs-component-ratom-relation

Docs - How ratom tracks the components that deref it
This commit is contained in:
Juho Teperi 2018-08-05 18:14:53 +03:00 committed by GitHub
commit 51818ce0cb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 6 deletions

View File

@ -175,8 +175,37 @@ Reactions are what give `r/atom`, `r/cursor`, and function `r/cursor` and `r/wra
* `on-set` and `on-dispose` are run when the reaction is set and unset from the DOM * `on-set` and `on-dispose` are run when the reaction is set and unset from the DOM
* `derefed` **TODO unclear** * `derefed` **TODO unclear**
**TODO EXAMPLE** Reactions are very useful when
* You need a way in which components only updates based on part of the ratom state. (reagent/cursor can also be used for this scenario)
* When you want to combine two `ratoms` and produce a result
* You want the component to use some transformed value of `ratom`
Here's an example:
```
(def app-state (reagent/atom {:state-var-1 {:var-a 2
:var-b 3}
:state-var-2 {:var-a 7
:var-b 9}}))
(def app-var2-reaction (reagent.ratom/make-reaction
#(get-in @app-state [:state-var-2 :var-a])))
(defn component-using-make-reaction []
[:div
[:div "component-using-make-reaction"]
[:div "Sate 2 - var a : " @app-var2-reaction]])
```
The below example uses `reagent.ratom/reaction` macro, which provides syntactic sugar around creating reaction using `make-reaction`
```
(let [username (reagent/atom "")
password (reagent/atom "")
fields-populated? (reagent.ratom/reaction (every? not-empty [@username @password]))]
[:div "Is username and password populated ?" @fields-populated?])
```
Reactions are executed asynchronously, so be sure to call `flush` if you depend on reaction side effects. Reactions are executed asynchronously, so be sure to call `flush` if you depend on reaction side effects.
## The track function ## The track function

View File

@ -86,7 +86,14 @@
;;; Rendering ;;; Rendering
(defn wrap-render [c] (defn wrap-render
"Calls the render function of the component `c`. If result `res` evaluates to a:
1) Vector (form-1 component) - Treats the vector as hiccup and returns
a react element with a render function based on that hiccup
2) Function (form-2 component) - updates the render function to `res` i.e. the internal function
and calls wrap-render again (`recur`), until the render result doesn't evaluate to a function.
3) Anything else - Returns the result of evaluating `c`"
[c]
(let [f ($ c :reagentRender) (let [f ($ c :reagentRender)
_ (assert-callable f) _ (assert-callable f)
res (if (true? ($ c :cljsLegacyRender)) res (if (true? ($ c :cljsLegacyRender))

View File

@ -33,10 +33,20 @@
false)))))) false))))))
(defn- in-context [obj f] (defn- in-context [obj f]
"When f is executed, if (f) derefs any ratoms, they are then added to 'obj.captured'(*ratom-context*).
See function notify-deref-watcher! to know how *ratom-context* is updated"
(binding [*ratom-context* obj] (binding [*ratom-context* obj]
(f))) (f)))
(defn- deref-capture [f r] (defn- deref-capture
"Returns `(in-context f r)`. Calls `_update-watching` on r with any
`deref`ed atoms captured during `in-context`, if any differ from the
`watching` field of r. Clears the `dirty?` flag on r.
Inside '_update-watching' along with adding the ratoms in 'r.watching' of reaction,
the reaction is also added to the list of watches on each ratoms f derefs."
[f r]
(set! (.-captured r) nil) (set! (.-captured r) nil)
(when (dev?) (when (dev?)
(set! (.-ratomGeneration r) (set! generation (inc generation)))) (set! (.-ratomGeneration r) (set! generation (inc generation))))
@ -48,7 +58,11 @@
(._update-watching r c)) (._update-watching r c))
res)) res))
(defn- notify-deref-watcher! [derefed] (defn- notify-deref-watcher!
"Add `derefed` to the `captured` field of `*ratom-context*`.
See also `in-context`"
[derefed]
(when-some [r *ratom-context*] (when-some [r *ratom-context*]
(let [c (.-captured r)] (let [c (.-captured r)]
(if (nil? c) (if (nil? c)
@ -337,7 +351,15 @@
(defn- handle-reaction-change [this sender old new] (defn- handle-reaction-change [this sender old new]
(._handle-change this sender old new)) (._handle-change this sender old new))
;; Fields of a Reaction javascript object
;; - auto_run
;; - captured
;; - caught
;; - f
;; - ratomGeneration
;; - state
;; - watches
;; - watching
(deftype Reaction [f ^:mutable state ^:mutable ^boolean dirty? ^boolean nocache? (deftype Reaction [f ^:mutable state ^:mutable ^boolean dirty? ^boolean nocache?
^:mutable watching ^:mutable watches ^:mutable auto-run ^:mutable watching ^:mutable watches ^:mutable auto-run
^:mutable caught] ^:mutable caught]
@ -499,7 +521,16 @@
(def ^:private temp-reaction (make-reaction nil)) (def ^:private temp-reaction (make-reaction nil))
(defn run-in-reaction [f obj key run opts]
(defn run-in-reaction
"Evaluates `f` and returns the result. If `f` calls `deref` on any ratoms,
creates a new Reaction that watches those atoms and calls `run` whenever
any of those watched ratoms change. Also, the new reaction is added to
list of 'watches' of each of the ratoms. The `run` parameter is a function
that should expect one argument. It is passed `obj` when run. The `opts`
are any options accepted by a Reaction and will be set on the newly created
Reaction. Sets the newly created Reaction to the `key` on `obj`."
[f obj key run opts]
(let [r temp-reaction (let [r temp-reaction
res (deref-capture f r)] res (deref-capture f r)]
(when-not (nil? (.-watching r)) (when-not (nil? (.-watching r))