One aspect of the improved code that you might be unfamiliar with is the use of .data[[input$var]] within renderPlot().
This is a useful feature from rlang that can be used across much of the tidyverse to reference columns in a data frame using a variable.
It helps avoid some of the complexity around “non-standard evaluation” (e.g. {{, !!, enquo(), etc.) when working with functions built with tidyeval (e.g. dplyr and ggplot2).
Reactive graph
With these additions, what should our reactive graph look like now?
Your turn - Exercise 03
Starting with the code in exercises/ex03.R (based on demo02.R’s code) add a tableOutput() with id minmax to the app’s mainPanel().
Once you have done that you should then add logic to the server function to render a table that shows the min and max temperature for each year contained in these data.
You will need to add an appropriate output in the ui
and a corresponding reactive expression in the server function to generate these summaries.
lubridate::year() will be useful along with dplyr::group_by() and dplyr::summarize().
12:00
Reactive graph (again)
reactlog
Another (more detailed) way of seeing the reactive graph (dynamically) for your app is using the reactlog package.
Run the following to log and show all of the reactive events occuring within ex03_soln.R,
This is an example of a “reactive conductor” - it is a new type of reactive expression that exists between sources (e.g. an input) and endpoints (e.g. an output).
As such, a reactive() depends on various upstream inputs, returns a value of some kind which used by 1 or more downstream outputs (or other conductors).
Their primary use is similar to a function in an R script, they help to
avoid repeating ourselves
decompose complex computations into smaller / more modular steps
improve computational efficiency by breaking up / simplifying reactive dependencies
reactive() tips
Expressions are written in the same way as render*() functions
If react_obj = reactive({...}) then any consumer must access value using react_obj() and notreact_obj
think of react_obj as a function that returns the current value
Common cause of everyone’s my favorite R error ,
## Error: object of type 'closure' is not subsettable`
Like input reactive expressions may only be used within reactive contexts (e.g. render*(), reactive(), observer(), etc.)
## Error: Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
Reactive graph
observers
observer()
These are constructed in the same way as a reactive() however an observer does not return a value, instead they are used for their side effects.
The side effects in most cases involve sending data to the client broswer, e.g. updating a UI element
While not obvious given their syntax - the results of the render*() functions are observers.
You may have notices that the App initializes with “West” selected for region but no initial selection for name because of this we have some warnings generated in the console:
Warning: There were 2 warnings in`summarize()`.The first warning was:ℹ In argument:`min temp = min(temp_min)`.Caused by warning in`min()`:! no non-missing arguments to min; returning Infℹ Run dplyr::last_dplyr_warnings() to see the 1 remaining warning.
This can be a common occurrence with Shiny, particularly at initialization or when a user enters bad / unexpected input(s).
A good way to protect against this is to validate your inputs - the simplest way is to use req() which checks if a value is truthy.
Non-truthy values prevent further execution of the reactive code (and downstream consumer’s code).
Your turn - Exercise 04
Using the code provided in exercise/ex04.R (based on demo/demo05.R) as a starting point add the calls to req() necessary to avoid the initialization warnings.
Also, think about if there are any other locations in our app where req() might be useful.
Hint - thinking about how events “flow” through the reactive graph will be helpful here.
10:00
A note on observers
Reactive graphs are meant to be acyclic, that is they should not have circular dependencies.
The use of observers can introduce cycles which can then lead to infinite loops, see the following example: