forked from hadley/mastering-shiny
-
Notifications
You must be signed in to change notification settings - Fork 0
/
reactivity-components.Rmd
259 lines (171 loc) · 13.5 KB
/
reactivity-components.Rmd
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
# Reactive components {#reactivity-components}
```{r setup, include=FALSE}
source("common.R")
```
## Introduction
## Building blocks {#primitives}
While there are lots of reactive programming related functions in Shiny, there are three objects that almost all of them build on, both conceptually and literally. We call these __reactive primitives__ because they are a fundamental part of the reactive framework and can not be implemented from simpler components.
* Reactive values, used to implement reactive inputs.
* Expressions
* Observers, used to implements reactive outputs.
```{r, echo = FALSE, out.width = NULL}
knitr::include_graphics("diagrams/basic-reactivity/producers-consumers.png", dpi = 300)
```
I assume that you're already familiar with the basic operation of these components, so here we'll explore a little bit more that data structures that make them tick.
We'll start by talking about reactive values, jump ahead to observers, then talk about reactive expressions. We'll finish up by discussing reactive outputs, which are a special type of observer.
For the remainder of the chapter I'll be running with a reactive console, so if you're following along on your own computer, you'll need to enable that too:
```{r}
consoleReactive(TRUE)
```
### Reactive values: values that change over time
You're already familiar with `inputs`, the list of reactive values that Shiny uses to communicate user actions in the browser to your R code. Inputs are a special read-only[^write-input] type of reactive values. Here we'll talk about the underlying reactive value primitive, which you can use in other ways.
[^write-input]: You can't write to reactive values in the `inputs`, but Shiny can. Behind the scenes, any time a user performs an action in the browser, Shiny updates these values. They're read only to you because Shiny wants to avoid the possibility of inconsistencies between the browser and R.
Shiny provides a special syntax for reactive values, because we need some way to update them over time, and `<-` blows away the existing object. You can create a single reactive `reactiveVal()` allows you to do this for a single value. A reactive value is a special type of function that returns its current value when called without arguments, and updates its value when called with a single argument. It's a magical box.
```{r}
val <- reactiveVal(10)
# Set
val(20)
# Get
val()
```
When handling multiple reactive values `reactiveValues()` which allows you to work with multiple reactive values, as if you have a list. The interface is more natural for complicated reasons.
```{r}
vals <- reactiveValues(x = 1, y = 2)
# Set
vals$y <- 100
# Get
vals$x
```
The big difference between reactive values and ordinary R values is that reactive values track who accesses them. And then when the value changes, it automatically lets everyone know that there's been a change. If a read of a regular variable is asking "What's the value of `x`?", reading a reactive value is asking "What's the value of `input$x`? **And please notify me the next time `input$x` changes!**" In other words, a reactive read has implications for both _now_ (returns the current value) and _later_ (notifies of the next change to the value).
We'll come back to that in detail in Chapter \@ref(dependency-tracking).
Now let's shift gears and talk about the objects that _can_ read reactive values, and what will happen when they're notified of changes in `input$x`. There are two fundamental types of reactive consumers in Shiny. One type is for actions (with side effects), the other is for calculations (no side effects).
:::sidebar
Almost all R functions are either __calculations__ or __actions__:
1. Calculation return a value: e.g. `sum()`, `mean()`, `read.csv()`.
1. Actions change the world in some way: e.g. `print()`, `plot()`, `write.csv()`.
In programming terminology, changing the world is called a __side-effect__. Unlike pharmaceuticals where side effects are always unintentional and usually negative, we simply mean any effects apart from a function's return value. Changing a file on disk is a side effect. Printing words to the console is a side effect. Sending a message to another computer is a side effect.
:::
### Observers: Automatic actions
Observers are reactive consumers that take a code block that performs an action of some kind. Observers are reactive consumers because they know how to respond to one of their dependencies changed: they re-run their code block. Here's an observer that prints the value of `x` every time it changes:
```{r}
x <- reactiveVal(10)
observe({
message("`x` is ", x())
})
x(5)
x(10)
```
This observer does two things. It prints out a message giving the current value of x, _and_ it subscribes to be notified of the next change to `x()`. When `x` changes, and this observer is notified, it requests that the Shiny runtime run its code block again, and two steps repeat.
It's important to understand that the subscription is not permanent, but happens multiple times, and it happens dynamically. That means that you can conditionally subscribe. TODO: Add example.
Note that observers force eager evaluation of the reactive expressions that they refer to.
Observers aren't limited to reading a single reactive value; each observer can read zero, one, or multiple reactive values.
### Reactive expressions: Smart calculations
Reactive expressions are the other fundamental type of reactive consumer. While observers model actions that have side effects, reactive expressions model calculations that return values. (There's nothing that prevents you from putting side effects in your reactive expressions, but it's generally a bad idea.)
Here's a very simple reactive expression named `up_to_x` that generates a sequence of numbers based on `input$x`. (If you're not familiar with `seq_len`, it simply returns a sequence of increasing numbers starting from 1 to whatever number you pass it; for example, `seq_len(3)` returns `c(1L, 2L, 3L)`.)
```{r}
up_to_x <- reactive({
seq_len(x())
})
```
The mere act of creating this reactive expression doesn't cause any code to execute. Rather, it just means that this sequence of numbers is available for retrieval, by calling `up_to_x()` like its a function. In this sense, creating a reactive expression is like declaring an R function: nothing actually happens until you call it.
In the following snippet, the code contained in `up_to_x` (from the above snippet) is not executed until the line `print(up_to_x())` is reached, as this is the first time the result of `up_to_x` is actually requested. (Because of this property, we say that reactive expressions are _lazy_ as opposed to _eager_.)
```{r}
observe({
print(up_to_x())
})
x(4)
```
This observer prints the sequence to the console whenever `up_to_x` changes (i.e. whenever `input$x` changes, because `up_to_x` reads `input$x`).
Just like with reading reactive values, reactive expressions are only readable by reactivity-aware consumers, and for the same reason: because `up_to_x()` is more than just "Can you calculate the current value of `up_to_x`?"; instead, it's "Can you calculate the current value of `up_to_x`? _And also notify me if something about this sequence changes?_"
So far we've learned that reactive expressions are _reactive_: they know when the reactive values they've read have changed, and they alert their readers when their own value may have changed. They're also _lazy_: they contain code, but that code doesn't execute unless/until someone tries to actually retrieve the value of the reactive expression (by calling it like a function).
The final important property of reactive expressions is that they _cache_ their most recent value. If you're not familiar with the term "cache", it means keeping a previously retrieved (or in this case, calculated) result in hand so that it can be used to satisfy future requests.
The first time a reactive expression is called, it will execute its code body, and depending on what that code does, it might take a significant amount of time. But when the calculation is complete, the resulting value will be both returned to the caller _and_ remembered by the reactive expression. Subsequent calls to the reactive expression take essentially no time at all, as the saved value can be returned instantly. If a reactive expression depends on reactive values or expressions, then any changes to those will cause the cached value to be discarded. When that happens, the next call to the reactive expression will again cause an actual calculation, whose result will then be saved for subsequent calls.
These particular properties -- laziness, caching, reactivity, and lack of side effects -- combine to give us an elegant and versatile building block for reactive programming.
## Inputs and outputs
### Inputs
### Outputs
You may wonder how Shiny outputs fit into this picture. By outputs, I'm referring to code like this:
```{r eval=FALSE}
output$text <- renderText({
paste(up_to_x(), collapse = ", ")
})
```
Is this an observer or a reactive expression? It looks like a reactive expression because we're assigning the result of `renderText()`, but as you've seen previously, `output` is write-only: you can't retrieve the value.
The answer is neither, per se. Reactive expressions and observers (and reactive values) are _primitives_ of reactive programming, meaning, they are fundamental building blocks. Outputs, on the other hand, are a feature of Shiny that is built on top of those reactive primitives. The details of how they are implemented are not that important, but it is important to know their characteristics.
Most importantly, outputs are reactive consumers. Output code is allowed to read reactive values like `input$x` or reactive expressions like `up_to_x()`, and the output will know when those reactive dependencies change.
Whereas observers execute eagerly and reactive expressions execute lazily, outputs are somewhere in between. When an output's corresponding UI element is visible in the browser, outputs execute eagerly; that is, once at startup, and once anytime their relevant inputs or reactive expressions change. However, if their UI element becomes hidden (e.g. it is located on a `tabPanel` that is not active, or `removeUI` is called to actively remove it from the page) then Shiny will automatically suspend (pause) that output from reactively executing. (In rare cases, you may prefer to process even outputs that aren't hidden. You can use the `outputOptions()` function's `suspendWhenHidden` to opt out of the automatic suspension feature on an output-by-output basis.)
We also know that observers should be used for side effects (actions), and reactive expressions for their return values (calculations). Again, outputs are somewhere in between. Depending on the `renderXXX` function you use to wrap it, your render code block may need to return a value and/or perform a side effect. For example, `renderText` expects you to return a string, while `renderPrint` expects you to make calls to `print()`; and `renderPlot` expects you to either draw a plot to the active graphics device or return a plottable object (like a ggplot2 object).
Generally, you shouldn't use arbitrary side effects in an output. Outputs are designed to capture output side-effects like printing and plotting. In an ideal world this might not be necessary.
Though outputs allow (and may even require) side effects, this doesn't mean you should include just any side effects in your output code. Shiny assumes that the whole code block of an output exists only in service of populating that output. If your output code block contains logic whose side effects are important for reasons apart from the actual output, you should extract that logic into a separate observer. That way, you can be confident it will execute regardless of whether the output is visible or not, now or in the future.
## Creating components
<https://github.com/hadley/shinySignals/>
### Isolate
Generally, you don't need to use `reactiveValues()` yourself. But then it can be useful for achieving specific types of coordination that would otherwise be inaccessible, particularly maintaining state. You need to be extra careful when reading to and writing from `reactiveValues()` because unlike the rest of Shiny, there's nothing to stop you from getting caught in an infinite loop.
```{r}
count <- function(signal) {
val <- reactiveVal(0)
observe({
signal()
val(val() + 1)
})
reactive(val())
}
```
```{r}
count <- function(signal) {
val <- reactiveVal(0)
observeEvent(signal(), {
val(val() + 1)
})
reactive(val())
}
```
```{r}
count <- function(signal) {
val <- reactiveVal(0)
observe({
signal()
val(isolate(val()) + 1)
})
reactive(val())
}
```
Whenever you modify a reactive value based on a previous value you'll need to use isolate to avoid getting stuck in an infinite loop.
### Temporal dependence
```{r}
dropRepeats <- function(signal) {
val <- reactiveVal()
observe({
cur_val <- isolate(val())
new_val <- signal()
if (!identical(cur_val, new_val)) {
val(new_val)
}
})
reactive(val)
}
```
<https://plotly-r.com/linking-views-with-shiny.html#reactive-vals>
```{r}
brush <- function(n, mode = "|") {
selected <- reactiveVal(rep(FALSE, n))
list(
brush = function(new) {
selected(do.call(mode, isolate(selected()), new))
},
reset = function() {
selected(rep(FALSE, n))
},
selected = reactive(selected)
)
}
```
### Combining reactives
```{r}
merge <- function(signal1, signal2) {
val <- reactiveVal()
observe(val(signal1()))
observe(val(signal2()))
reactive(val())
}
```