Chapter 23 Reactive components
23.2 Building blocks
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.
- Observers, used to implements reactive outputs.
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:
23.2.1 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-only41 type of reactive values. Here we’ll talk about the underlying reactive value primitive, which you can use in other ways.
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.
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.
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 21.
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).
23.2.2 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:
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 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.
23.2.3 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,
c(1L, 2L, 3L).)
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.)
This observer prints the sequence to the console whenever
up_to_x changes (i.e. whenever
input$x changes, because
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.
23.3 Inputs and outputs
You may wonder how Shiny outputs fit into this picture. By outputs, I’m referring to code like this:
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
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
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.
23.4 Creating components
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.
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.
23.4.2 Temporal dependence
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.↩︎