You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: docs/FAQs/UseASubscriptionInAnEventHandler.md
+6-4
Original file line number
Diff line number
Diff line change
@@ -13,10 +13,12 @@ How do I access the value of a subscription from within an event handler?
13
13
Subscriptions are stateful. That said, they offer a 90% solution where you don't have to worry about their state.
14
14
But this comes with a caveat: **the only safe place to call `subscribe` is within a reagent component function.**
15
15
16
-
See [Flows - Reactive Context](/re-frame/flows-advanced-topics/#reactive-context)
17
-
for an in-depth explanation.
16
+
!!! Note
17
+
See [Flows - Reactive Context](/re-frame/flows-advanced-topics/#reactive-context)
18
+
for an in-depth explanation.
18
19
19
20
### DOM event handlers
21
+
20
22
Inner functions, such as DOM event handlers, don't count. With a hiccup like `[:button {:on-click #(subscribe [:some (gensym)])}]`, we expect the browser to call our `#(subscribe [:some (gensym)])` function at some later time. The problem is, reagent and re-frame have no way to safely manage your subscription at that time. The result is a memory leak. If your browser calls this function a thousand times, re-frame will "create" a thousand subscriptions, and there's no code in place to "dispose" them later.
21
23
22
24
### Re-frame event handlers
@@ -49,7 +51,7 @@ This isn't a real solution, it's just incidental safety.
49
51
50
52
### Restructure your app
51
53
52
-
Sometimes it's enough to factor our your calculations, so they can be shared between subscription and event handlers.
54
+
Sometimes it's enough to factor out your calculations, so they can be shared between subscription and event handlers.
53
55
54
56
#### *Don't* call `subscribe` in your event handler:
55
57
@@ -68,7 +70,7 @@ Sometimes it's enough to factor our your calculations, so they can be shared bet
Copy file name to clipboardexpand all lines: docs/Flows.md
+53-57
Original file line number
Diff line number
Diff line change
@@ -19,7 +19,7 @@ More concretely, when the values change at one or more paths within `app-db`,
19
19
then the value at another path is "automatically" recalculated.
20
20
21
21
## Why do we need flows?
22
-
We turn to flows when we need a dynamic relationship between values - a "difference which makes a difference" ([Bateson](http://faculty.washington.edu/jernel/521/Form.htm)).
22
+
We turn to flows when we need a dynamic relationship between values - a ["difference which makes a difference"](http://faculty.washington.edu/jernel/521/Form.htm).
23
23
24
24
For instance, how would you model this problem?
25
25
@@ -38,7 +38,7 @@ We think flows offer a [Better Way](/re-frame/flows-advanced-topics#a-better-way
38
38
!!! Note "The DataFlow Paradigm"
39
39
Dataflow programming emerged in the 1970s, so it is almost as foundational as functional programming.
40
40
Indeed, reactive programming - so much the rage these days - is simply a subset of dataflow programming.
41
-
In contrast with imperative building blocks like 'if/then', 'next' and 'goto',
41
+
In contrast with imperative building blocks like `if/then`, `next` and `goto`,
42
42
dataflow programming implements control flow via the propagation of change.
43
43
Both the functional and dataflow paradigms have profoundly influenced the design of re-frame.
@@ -242,11 +242,12 @@ Many such tasks amount to synchronization - maintaining an invariant within a ch
242
242
And of course, a task which seems complex may just be a chain of simple tasks.
243
243
244
244
One relatable example is that of trying to maintain cascading error states. Imagine your UI has a validation rule: `start date` must be before `end date`.
245
-
After the user changes either value, the error state must be calculated. This is used to determine if the `submit` button is enabled or not, and if an error message is displayed or not.
245
+
After the user changes either value, the error state must be calculated.
246
+
The result indicates whether to enable the submit button or display an error message.
246
247
247
-
Now, imagine your UI has many validation rules, and an error state must be calculated for each of them.
248
-
In this case, the submit button state is a secondary calculation which combines these error states.
249
-
Cascading, derived values.
248
+
Now, imagine your UI has many validation rules, each with its own error state.
249
+
In this case, the submit button state is a secondary calculation which combines these error states.
250
+
Cascading, derived values.
250
251
251
252
Data flows from the leaves (what the user entered), through intermediate nodes (error predicate functions), through to the root (submit button state).
252
253
Both the intermediate values and the root value are important.
@@ -280,6 +281,7 @@ Just like with layered subscriptions, one flow can use the value of another. Rem
280
281
</div>
281
282
282
283
## Subscribing to flows
284
+
283
285
In our examples so far, we've used a regular subscription, getting our flow's output path.
284
286
In `re-frame.alpha`, you can also subscribe to a flow by name.
285
287
This bypasses the [caching behavior](/re-frame/flows-advanced-topics#caching) of a standard subscription.
@@ -335,21 +337,7 @@ For this, we use a `:live?` function.
335
337
The quote above deals with phenomenal life, but you can also think of `:live?` as in a tv or internet broadcast.
336
338
Data flows, but only when the flow itself is live.
A barebones tab picker, and something to show us the value of `app-db`:
340
+
Let's try it out. For example, here's a barebones tab picker, and something to show us the value of `app-db`:
353
341
354
342
<divclass="cm-doc">
355
343
(def tabs [:kitchen:garage])
@@ -382,15 +370,25 @@ A barebones tab picker, and something to show us the value of `app-db`:
382
370
383
371
### Live?
384
372
385
-
Here's a more advanced version of our kitchen calculator flow.
386
-
This replaces our first `:kitchen-area` flow, since it has the same `:id`:
387
-
373
+
Here's a more advanced version of our room calculator flow.
388
374
389
-
Notice the different types of inputs. `:w [:kitchen :width]` represents an input as an `app-db` path, while `:tab :current-tab` identifies the value from the `:current-tab` flow we defined earlier.
Also, notice the new `:tab` input, and the new `:live?`.
387
+
Notice the new `:live-inputs` and `:live?` keys.
388
+
Just like `:output`, `:live:?` is a function of the resolved `:live-inputs`.
392
389
393
-
Just like `:output`, `:live:?` is a function of `app-db` and the `:inputs`. Re-frame only calculates the `:output` when the `:live?` function returns a truthy value. Otherwise, the flow is presumed dead.
390
+
Re-frame only calculates the `:output` when the `:live?` function returns a truthy value.
391
+
Otherwise, the flow is presumed dead.
394
392
395
393
Let's test it out:
396
394
@@ -410,7 +408,8 @@ Let's test it out:
410
408
411
409
<divid="tabbed-app"></div>
412
410
413
-
Try switching tabs. Notice how `:area` only exists when you're in the `room-calculator` tab. What's happening here?
411
+
Try switching tabs.
412
+
Notice how the path `[:kitchen :area]` only exists when you're in the `room-calculator` tab. What's happening here?
414
413
415
414
### Lifecycle
416
415
@@ -419,14 +418,12 @@ Depending on the return value of `:live?`, re-frame handles one of 4 possible st
419
418
420
419
| transition | action |
421
420
|---|---|
422
-
| From **live** to **live**| run `:output`|
423
-
| From **dead** to **live**| run `:init` and `:output`|
421
+
| From **live** to **live**| run `:output`(when `:inputs` have changed) |
422
+
| From **dead** to **live**| run `:output`|
424
423
| From **live** to **dead**| run `:cleanup`|
425
424
| From **dead** to **dead**| do nothing |
426
425
427
-
Basically, *living* flows get output, *dying* flows get cleaned up, *arising* flows get initiated and output.
428
-
429
-
And independently of all this, `:output` only runs when `:inputs` have changed value.
426
+
Basically, *arising* flows get output, *living* flows get output as needed, and *dying* flows get cleaned up.
430
427
431
428
### Cleanup
432
429
@@ -447,42 +444,42 @@ The point is, *you* express when the signal lives or dies, not your render tree.
447
444
448
445
## Redefining and Undefining
449
446
450
-
Not only do flows have a lifecycle (defined by `:live?`, `:init` and `:cleanup`), but this lifecycle also includes registration and deregistration.
447
+
Not only do flows have a lifecycle (defined by `:live?` and `:cleanup`), but this lifecycle also includes registration and deregistration.
451
448
452
449
- When you call `reg-flow`, that flow comes alive.
453
-
-`:init` and `:output`run, even if the inputs haven't changed.
450
+
-`:output`runs, even if the inputs haven't changed.
454
451
- That's because the flow itself has changed.
455
452
- When you call `clear-flow`, it dies (running `:cleanup`).
456
453
- Re-frame provides `:reg-flow` and `:clear-flow`[effects](#re-frame/Effects/) for this purpose.
457
454
458
455
Here's another demonstration. Think of it as a stripped-down todomvc.
459
456
You can add and remove items in a list:
457
+
460
458
<divclass="cm-doc">
461
-
(rf/reg-sub ::items :-> (comp reverse ::items))
459
+
(rf/reg-sub :items :-> (comp reverse :items))
462
460
463
461
(rf/reg-event-db
464
462
::add-item
465
-
(fn [db [_ id]] (update db ::items conj id)))
463
+
(fn [db [_ id]] (update db :items conj id)))
466
464
467
465
(rf/reg-event-db
468
466
::delete-item
469
-
(fn [db [_ id]] (update db ::items #(remove #{id} %))))
467
+
(fn [db [_ id]] (update db :items #(remove #{id} %))))
0 commit comments