Repository navigation
Expand file tree
/
Copy path15-reactive-building-blocks.qmd
More file actions
869 lines (599 loc) · 27.5 KB
/
Copy path15-reactive-building-blocks.qmd
File metadata and controls
869 lines (599 loc) · 27.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
# Reactive building blocks {#sec-chap15}
```{r}
#| label: setup
#| results: hold
#| include: false
base::source(file = "R/helper.R")
library(glossary)
glossary::glossary_path("../glossary-pb/glossary.yml")
library(shiny)
reactiveConsole(TRUE)
```
## Table of content for chapter 15 {.unnumbered}
::::: {#obj-chap03}
:::: {.my-objectives}
::: {.my-objectives-header}
Chapter section list
:::
::: {.my-objectives-container}
:::
::::
:::::
## Reactive values
There are two types of reactive values:
- A single reactive value, created by `reactiveVal()`.
- A list of reactive values, created by `reactiveValues()`.
They have slightly different interfaces for getting and setting values.
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-reactive-values-interface}
: Interfaces for getting and setting reactive values
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-reactive-values-interface}
```{r}
#| label: reactive-values-interface
#| results: hold
x <- reactiveVal(10)
x() # get
x(20) # set
x() # get
r <- reactiveValues(x = 10)
r$x # get
r$x <- 20 # set
r$x # get
```
`reactiveVal()` and `reactiveValues()` have different interfaces
:::
::::
:::::
While they look different, they behave the same, so you can choose between them based on which syntax you prefer. In this book Hadley use `reactiveValues()` because the syntax is easier to understand at a glance, but in his own code h tends to use `reactiveVal()` because the syntax makes it’s clear that something weird is going on.
It’s important to note that both types of reactive values have so called reference semantics. Most R objects have copy-on-modify semantics which means that if you assign the same value to two names, the connection is broken as soon as you modify one.
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-copy-on-modify-semantics}
: Copy on modify semantics in R
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-copy-on-modify-semantics}
```{r}
#| label: copy-on-modify-semantics
a1 <- a2 <- 10
a2 <- 20
a1 # unchanged
```
Copy on modify semantics in R
:::
::::
:::::
Reactive values do not have copy-on-modify semantics. They always keep a reference back to the same value so that modifying any copy modifies all values:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-reference-semantics}
: Reference semantics of reactive values
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-reference-semantics}
```{r}
#| label: reference-semantics
#| results: hold
b1 <- b2 <- reactiveValues(x = 10)
glue::glue("Start: b1 and b2 have the same value. b1$x = ", {b1$x}, ", b2$x = ", {b2$x})
b1$x <- 20
glue::glue("Modification: Only b1$x has changed to ", {b1$x}, ", but b2$x has also changed to", {b2$x})
```
Reactive values have reference semantics
:::
::::
:::::
We’ll come back to why you might create your own reactive values in @sec-chap16. Otherwise, most of the reactive values you’ll encounter will come from the input argument to the server function. These are a little different to the `reactiveValues()` that you create yourself because they’re read-only: you can’t modify the values because Shiny automatically updates them based on user actions in the browser.
### Exercises {#sec-15-exercises-1}
#### Two list of reactive values
What are the differences between these two lists of reactive values? Compare the syntax for getting and setting individual reactive values.
:::::{.my-exercise}
:::{.my-exercise-header}
:::::: {#exr-15-two-lists-of-reactive-values}
: Exercise: Two lists of reactive values
::::::
:::
::::{.my-exercise-container}
::: {#lst-15-two-lists-of-reactive-values}
```{r}
#| label: two-lists-of-reactive-values
#| results: hold
l1 <- reactiveValues(a = 1, b = 2)
l2 <- list(a = reactiveVal(1), b = reactiveVal(2))
glue::glue("l1$a = {l1$a}")
glue::glue("l1$b = {l1$b}")
glue::glue("l2$a() = {l2$a()}")
glue::glue("l2$b() = {l2$b()}")
```
Exercise: Two lists of reactive values
:::
***
Content of `l1` are values, content of `l2` are functions
::::
:::::
#### Has `reactiveVal()` reference semantics?
Design and perform a small experiment to verify that `reactiveVal()` also has reference semantics.
:::::{.my-exercise}
:::{.my-exercise-header}
:::::: {#exr-15-reactive-val-reference-semantics}
: Exercise: Has `reactiveVal()` reference semantics?
::::::
:::
::::{.my-exercise-container}
::: {#lst-15-reactiveVal-reference-semantics}
```{r}
#| label: reactive-val-reference-semantics
r1 <- r2 <- reactiveVal(10)
r1(20)
r2()
```
Exercise: Has `reactiveVal()` reference semantics?
:::
***
Yes, `reactiveVal()` also has reference semantics: I initialized r1 & r2 with `reactiveVal(10)`, then I changed only to value of `r1(20)`- Printing the content of `r2()` results also in 20, although I hadn't change `r2()`directly.
::::
:::::
## Reactive expressions
Recall that a reactive has two important properties: it’s lazy and cached. This means that it only does work when it’s actually needed, and if called twice in a row, it returns the previous value.
There are two important details that we haven’t yet covered: what reactive expressions do with errors, and why `base::on.exit()` works inside of them.
### Errors
Reactive expressions cache errors in exactly the same way that they cache values. For example, take this reactive:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-cache-errors}
: Reactive expressions cache errors
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-cache-errors}
```{r}
#| label: cache-errors1
#| error: true
r <- reactive(stop("Error occured at ", Sys.time(), call. = FALSE))
r()
```
```{r}
#| label: cache-errors2
#| error: true
Sys.sleep(2)
r()
```
Reactive expressions cache errors
:::
::::
:::::
Errors are also treated the same way as values when it comes to the reactive graph: errors propagate through the reactive graph exactly the same way as regular values. The only difference is what happens when an error hits an output or observer:
- An error in an output will be displayed in the app.
- An error in an observer will cause the current session to terminate. If you don’t want this to happen, you’ll need to wrap the code in `try()` or `tryCatch()`.
This same system powers `req()` (@sec-08-cancelling-execution), which emits a special type of error. This special error causes observers and outputs to stop what they’re doing but not otherwise fail. By default, it will cause outputs to reset to their initial blank state, but if you use `req(..., cancelOutput = TRUE)` they’ll preserve their current display.
### `on.exit()`
You can think of `reactive(x())` as a shortcut for `function() x()`, automatically adding laziness and caching. This is mostly of importance if you want to understand how Shiny is implemented, but means that you can use functions that only work inside functions. The most useful of these is `on.exit()` which allows you to run code when a reactive expression finishes, regardless of whether the reactive successfully returns an error or fails with an error. This is what makes `on.exit()` work in @sec-08-removing-on-completion.
### Exercises {#sec-15-exercises-2}
#### Error propagation with `stop()`
Use the {**reactlog**} package to observe an error propagating through the reactives in the following app, confirming that it follows the same rules as value propagation.
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-error-propagation1}
: Error propagation
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-error-propagation1}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 450
#| components: [editor, viewer]
## file: app.R
{{< include apps_15/01-ex-error-propagation/app.R >}}
```
Analyzing the error propagation using `stop()` with {**reactlog**}
:::
::::
:::::
I added the line `reactlog::reactlog_enable()` to the app code, started the app and ticked the check box "error?". In the console I received
> Warning: Error in \<reactive:a\>: Error
I started {**reactlog**} with the {{< kbd Cmd-F3 >}} shortcut and analyzed the propagation. I could confirm that the error followed the same rules as the value.
#### Error propagation with `req()`
Modify the above app to use `req()` instead of `stop()`. Verify that events still propagate the same way. What happens when you use the `cancelOutput` argument?
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-error-propagation2}
: Error propagation
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-error-propagation2}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 450
#| components: [editor, viewer]
## file: app.R
{{< include apps_15/02-ex-error-propagation/app.R >}}
```
Analyzing the error propagation using `req()` with {**reactlog**}
:::
::::
:::::
- Using `req()` in the `if()` condition does not `3` as in @lst-15-error-propagation2 out doesn't output anything. As the condition `req() = TRUE` is not fulfilled Shiny stops the execution.
- Using `req(…, cancelOutput = TRUE)` shows for me no difference. It is said the in the documentation that
> it is treated slightly differently if one or more outputs are currently being evaluated. In those cases, the reactive chain does not proceed or update, but the output(s) are left is whatever state they happen to be in (whatever was their last valid state).
But in the `reactlog` it shows me the new condition (`input$error 'logo TRUE`). But reading the next pargraph in the docs gives an explanation:
> Note that this is always going to be the case if this is used inside an output context (e.g. `output$txt <- ...`). **It may or may not be the case if it is used inside a non-output context** (e.g. `reactive()`, `observe()` or `observeEvent()`) — depending on whether or not there is an `output$...` that is triggered as a result of those calls. (emphasis is mine)
## Observers and Outputs {#sec-15-observers-and-outputs}
Observers and outputs are terminal nodes in the reactive graph. They differ from reactive expressions in two important ways:
- They are eager and forgetful — they run as soon as they possibly can and they don’t remember their previous action. This eagerness is “infectious” because if they use a reactive expression, that reactive expression will also be evaluated.
- The value returned by an observer is ignored because they are designed to work with functions called for their side-effects, like `cat()` or `write.csv()`.
Observers and outputs are powered by the same underlying tool: `observe()`. This sets up a block of code that is run every time one of the reactive values or expressions it uses is updated. [Note that the observer runs immediately when you create it]{.mark} — it must do this in order to determine its reactive dependencies.
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-observe-test}
: Testing behavior of `observe()`
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-observe-test}
```{{r}}
#| label: observe-test
#| results: hold
y <- reactiveVal(10)
observe({
message("`y` is ", y())
})
y(5)
y(4)
```
Nut run: Testing behavior of `observe()`
:::
::::
:::::
Hadley rarely uses `observe()` in this book, because it’s the low-level tool that powers the user-friendly `observeEvent()`. Generally, you should stick with `observeEvent()` unless it’s impossible to get it to do what you want. In this book, there is only one case where `observe()` is necessary. See @sec-16-pausing-animations
`observe()` also powers reactive outputs. Reactive outputs are a special type of observers that have two important properties:
- They are defined when you assign them into output, i.e. `output$text <- ...` creates the observer.
- They have some limited ability to detect when they’re not visible (i.e. they’re in non-active tab) so they don’t have to recompute.
It’s important to note that `observe()` and the reactive outputs don’t “do” something, but “create” something (which then takes action as needed). That mindset helps you to understand what’s going on in this example:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-observe-calls-observe}
: `observe()` calls `observe()`
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-observe-calls-observe}
```{r}
#| label: observe-calls-observe
#| results: hold
x <- reactiveVal(1)
observe({
x()
observe(print(x()))
})
x(2)
x(3)
```
`observe()` calls `observe()`
:::
::::
:::::
::: {.callout-caution #cau-15-observe-without-value}
In contrast to the book example my observe() functions does not assign a value to `y`. This generates a
> Warning: Error in y: could not find function "y"
Observers do not yield a result, they are only useful for their side effects.
:::
Each change to `x` causes the observer to be triggered. The observer itself calls `observe()` setting up another observer. So each time `x` changes, it gets another observer, so its value is printed another time.
As a general rule, you should only ever create observers or outputs at the top-level of your server function. If you find yourself trying to nest them or create an observer inside an output, sit down and sketch out the reactive graph that you’re trying to create — there’s almost certainly a better approach. It can be harder to spot this mistake directly in a more complex app, but you can always use the `reactlog`: just look for unexpected churn in observers (or outputs), then track back to what is creating them.
## Isolating code
To finish off the chapter we need to discuss two important tools for controlling exactly how and when the reactive graph is invalidated. In this section, we’ll discuss `isolate()`, the tool that powers `observeEvent()` and `eventReactive()`, and that lets you avoid creating reactive dependencies when not needed. In the next section, you’ll learn about `invalidateLater()` which allows you to generate reactive invalidations on a schedule.
### `isolate()` {#sec-15-isolate}
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-infinite-loop-observer}
: Observer with infinite loop
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-infinite-loop-observer}
```{r}
#| label: infinite-loop-observer
#| eval: false
r <- reactiveValues(count = 0, x = 1)
observe({
r$x
r$count <- r$count + 1
on.exit()
# if (r$count > 10) stop("To many calls") # my addition
print(r$count) # my addition
})
```
Not run: Observer with infinite loop
:::
::::
:::::
If you were to run @lst-15-infinite-loop-observer, you’d immediately get stuck in an infinite loop because the observer will take a reactive dependency on `x` **and** `count`; and since the observer modifies count, it will immediately re-run.
I tried to run @lst-15-infinite-loop-observer with two addition: The first one tries to stop the loop after 10 rounds, the second one prints the result for each run.
But this didn't work. To stop a running process in Shiny is a complex enterprise as I have found out. Searching on the Internet I learned about some pointers to solve this problem. But here I have skipped this non-trivial learning task and prevented to run the infinite loop with `eval = false`
:::::{.my-resource}
:::{.my-resource-header}
:::::: {#lem-15-stop-ruuning-shiny-processes}
: How to stop running Shiny processes without killing the app
::::::
:::
::::{.my-resource-container}
- [StackOverflow answer](https://stackoverflow.com/a/76005099/7322615) with some important references
- [ExtendendTask](https://rstudio.github.io/shiny/reference/ExtendedTask.html) is a relatively new Shiny function about running long tasks in the background, which could possible also address the stop problem of Shiny process.
Additionally I found reference to other packages which could use to stop running processes:
- [{**callr**}](https://callr.r-lib.org/): Call R from R
- [{**future**}](https://future.futureverse.org/): Unified Parallel and Distributed Processing in R for Everyone
- [{**ipc**}](https://github.com/fellstat/ipc): Tools for Message Passing Between Processes
- [{**mirai**}](https://mirai.r-lib.org/): Minimalist Async Evaluation Framework for R
::::
:::::
Shiny provides `isolate()` to resolve the problem of undesired reactive dependency. This function allows you to access the current value of a reactive value or expression without taking a dependency on it:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-using-isolate}
: Accessing reactive value without taking a dependency on it
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-using-isolate}
```{r}
#| label: using-isolate
r <- reactiveValues(count = 0, x = 1)
r$count
class(r)
observe({
r$x
r$count <- isolate(r$count) + 1
})
r$x <- 1
r$x <- 2
r$count
r$x <- 3
r$count
```
Using `isolate()`: Accessing reactive values or reactive expressions without taking a dependency on it
:::
::::
:::::
Like `observe()`, a lot of the time you don’t need to use `isolate()` directly because there are two useful functions that wrap up the most common usage: `observeEvent()` and `eventReactive()`.
### `observeEvent()` and `eventReactive()`
When you saw the code above, you might have remembered @sec-03-observers and wondered why we didn’t use `observeEvent()`:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-replace-observe-with-observeEvent}
: Replace `observe()` with `observeEvent()`
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-replace-observe-with-observeEvent}
```{r}
#| label: replace-observe-with-observeEvent
observeEvent(r$x, {
r$count <- r$count + 1
})
```
To prevent an infinite loop with `observe()` in @lst-15-infinite-loop-observer you could use `isolate()` as in @lst-15-using-isolate, but also `observeEvent()`
:::
::::
:::::
Indeed, we could used `observeEvent(x, y)` because it is equivalent to` observe({x; isolate(y)})`. It elegantly decouples what you want to listen to from what action you want to take. And `eventReactive()` performs the analogous job for reactives: `eventReactive(x, y)` is equivalent to `reactive({x; isolate(y)})`.
`observeEvent()` and `eventReactive()` have additional arguments that allow you to control the details of their operation:
- By default, both functions will ignore any event that yields NULL (or in the special case of action buttons, 0). Use `ignoreNULL = FALSE` to also handle NULL values.
- By default both functions will run once when you create them. Use `ignoreInit = TRUE` to skip this run.
- For `observeEvent()` only, you can use `once = TRUE` to run the handler only once.
### Exercises {#sec-15-exercises-3}
#### Complete app
Complete the app below with a server function that updates `out` with the value of `x` only when the button is pressed.
:::{.column-page-inset}
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-complete-app-isolate}
: Complete app using `isolate()`
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-complete-app-isolate}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 400
#| components: [editor, viewer]
## file: app.R
{{< include apps_15/03-ex-complete-app-isolate/app.R >}}
```
App updates with the value of `x` only when the button is pressed
:::
::::
:::::
:::
## Timed invalidation
`isolate()` reduces the time the reactive graph is invalidated. This topic of this section, `invalidateLater()` does the opposite: it lets you invalidate the reactive graph when no data has changed. You saw an example of this in @sec-03-timed-invalidation with `reactiveTimer()`, but the time has come to discuss the underlying tool that powers it: `invalidateLater()`.
`invalidateLater(ms)` causes any reactive consumer to be invalidated in the future, after ms milliseconds. It is useful for creating animations and connecting to data sources outside of Shiny’s reactive framework that may be changing over time. For example, this reactive will automatically generate 10 fresh random normals every half a second:
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-observer-incrementing-number}
: Observer incrementing number
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-observer-incrementing-number}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 300
#| components: [editor, viewer]
library(shiny)
ui <- fluidPage(
textOutput("sum")
)
server <- function(input, output, session) {
sum <- reactiveVal(0)
observe({
invalidateLater(300)
sum(isolate(sum()) + runif(1))
})
output$sum <- renderText({
if (sum() > 10) stop()
sum()
})
}
shinyApp(ui, server)
```
Observer increments a cumulative sum with a random number and stops with number over 10
:::
::::
:::::
In the following sections you’ll learn how to use `invalidateLater()` to read changing data from disk, how to avoid getting `invalidateLater()` stuck in an infinite loop, and some occasionally important details of exactly when the invalidation happens.
### Polling
A useful application of `invalidateLater()` is to connect Shiny to data that is changing outside of R. For example, you could use the following reactive to re-read a csv file every second:
::: {#lst-15-re-reading-csv-file1}
```{{r}}
data <- reactive({
on.exit(invalidateLater(1000))
read.csv("data.csv")
})
```
Not run: Re-reading a CSV file with `invalidateLater()`
:::
This connects changing data into Shiny’s reactive graph, but it has a serious downside: when you invalidate the reactive, you’re also invalidating all downstream consumers, so even if the data is the same, all the downstream work has to be redone.
To avoid this problem, Shiny provides `reactivePoll()` which takes two functions: one that performs a relatively cheap check to see if the data has changed and another more expensive function that actually does the computation. We can use `reactivePoll()` to rewrite the previous reactive as below.
::: {#lst-15-re-reading-csv-file3}
```{{r}}
server <- function(input, output, session) {
data <- reactivePoll(1000, session,
function() file.mtime("data.csv"),
function() read.csv("data.csv")
)
}
```
Not run: Re-reading a CSV file with `reactivePoll()`
:::
Here we used to `file.mtime()`, which returns the last time the file was modified, as a cheap check to see if we need to reload the file:
Reading a file when it changes is a common task, so Shiny provides an even more specific helper with `reactiveFileReader()` that just needs a file name and a reader function:
::: {#lst-15-re-reading-csv-file3}
```{{r}}
server <- function(input, output, session) {
data <- reactiveFileReader(1000, session, "data.csv", read.csv)
}
}
```
Not run: Re-reading a CSV file with `reactiveFileReader()`
:::
If you need to read changing data from other sources (e.g. a database), you’ll need to come up with your own `reactivePoll()` code.
### Long running reactives
If you’re performing a long running computation, there’s an important question you need to consider: when should you execute `invalidateLater()`? For example, take this reactive:
::: {#lst-15-long-running-reactive1}
```{{r}}
server <- function(input, output, session) {
data <- reactiveFileReader(1000, session, "data.csv", read.csv)
}
```
Not run: Long running reactive with `invalidateLater()` time problem.
:::
Assume Shiny starts the reactive running at time 0, it will request invalidation at time 500. The reactive takes 1000ms to run, so it’s now time 1000, and it’s immediately invalidated and must be recomputed, which then sets up another invalidation: we’re stuck in an infinite loop.
On the other hand, if you run `invalidateLater()` at the end, it will invalidate 500ms after completion, so the reactive will be re-run every 1500 ms.
::: {#lst-15-long-running-reactive2}
```{{r}}
x <- reactive({
on.exit(invalidateLater(500), add = TRUE)
Sys.sleep(1)
10
})
```
Not run: Long running reactive with `invalidateLater()` time problem solved.
:::
This is the main reason to prefer `invalidateLater()` to the simpler `reactiveTimer()` that we used earlier: it gives you greater control over exactly when the invalidation occurs.
### Timer accuracy
The number of milliseconds specified in `invalidateLater()` is a polite request, not a demand. R may be doing other things when you asked for invalidation to occur, so your request has to wait. This effectively means that the number is a minimum and invalidation might take longer than you expect. In most cases, this doesn’t matter because small differences are unlikely to affect user perception of your app. However, in situations where many small errors will accumulate, you should compute the exact elapsed time and use it to adjust your calculations.
For example, the following code computes distance based on velocity and elapsed time. Rather than assuming `invalidateLater(100)` always delays by exactly 100 ms, we’ll compute the elapsed time and use it in the calculation of position.
::: {#lst-15-timer-accuracy}
```{{r}}
velocity <- 3
r <- reactiveValues(distance = 1)
last <- proc.time()[[3]]
observe({
cur <- proc.time()[[3]]
time <- last - cur
last <<- cur
r$distance <- isolate(r$distance) + velocity * time
invalidateLater(100)
})
```
Not run: Computing elapsed time for timer accuracy
:::
### Exercises {#sec-15-exercises4}
#### Why reactive never executed?
Why will this reactive never be executed? Your explanation should talk about the reactive graph and invalidation.
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-ex-reactive-never-executed1}
: Exercise: A reactive never executed
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-ex-reactive-never-executed1}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 550
#| components: [editor, viewer]
## file: app.R
{{< include apps_15/04-ex-reactive-never-executed1/app.R >}}
```
Exercise: The reactive is never executed because there is no output/observer and input/value to call the reactive expression invalidate. Shiny can't therefore start (re)-execution.
:::
::::
:::::
The reactive is never called. There is no input that can be invalidated and on the other hand, there is no reactive consumer (an output) to display changed values.
After initializing a session, Shiny can't pick an observer because there is none. As Shiny does not choose a reactive expression for the execution start, and there is no input that could be invalidated, the reactive can't never be called.
To see the difference I have complemented the app in @lst-15-ex-reactive-never-executed1 with an output. Now you can see that the reactive is executed.
:::{.column-page-inset}
:::::{.my-r-code}
:::{.my-r-code-header}
:::::: {#cnj-15-ex-reactive-never-executed2}
: Exercise: The reactive is this time executed
::::::
:::
::::{.my-r-code-container}
::: {#lst-15-ex-reactive-never-executed2}
```{shinylive-r}
#| standalone: true
#| viewerHeight: 550
#| components: [editor, viewer]
## file: app.R
{{< include apps_15/04-ex-reactive-never-executed2/app.R >}}
```
Exercise: The reactive can now be executed, because Shiny can invalidate the output/observer and the reactive can be called.
:::
::::
:::::
:::
::: {.callout-caution #cau-stop-app-not-working-with-shinylive-r}
###### `stopApp()` not working with `shinylive-r`
I had to use a more complex logic with an intermediate reactive value because the simpler solution with `stopApp()` inside an `observeEvent()` listening to the "stop" `actionButton()` does not work with `shinylive-r`.
:::
#### `reactivePoll()` with SQL
If you’re familiar with SQL, use `reactivePoll()` to only re-read an imaginary “Results” table whenever a new row is added. You can assume the Results table has a `timestamp` field that contains the date-time that a record was added.
As I am not familiar with `r glossary("SQL")` I will skip this exercise.
## Glossary Entries {#unnumbered}
```{r}
#| label: glossary-table
#| echo: false
glossary_table()
```
------------------------------------------------------------------------
## Session Info {.unnumbered}
::: my-r-code
::: my-r-code-header
Session Info
:::
::: my-r-code-container
```{r}
#| label: session-info
sessioninfo::session_info()
```
:::
:::