So far, we used render* functions to produce our output. All of the action with those functions happens inside of the app.
But sometimes, we want to reach outside the app: call an API, send data to a database, send debugging messages. We want to call a function for its “side-effects.”
We need observers for that!
Observers
Observers are functions that run when their dependencies change, just like outputs.
But unlike output rendering functions that directly update the UI, observers handle tasks that can affect the application state or perform other “side effects.”
Types of Observers
observe({}): Automatically re-executes when any of its reactive dependencies change.
observeEvent(event, { }): Executes in response to a specific event, offering more control over reactivity and allowing you to ignore changes in other inputs.
What Are Side Effects?
In the context of Shiny, a side effect refers to any action performed by an observer that affects the state of the application or external systems but does not directly result in UI output. These include:
# Observe changes to region selectionobserveEvent(input$regions, {if (input$regions =="Global") {# Reset the selected country and display global trend when "Global" is chosenselected_country(NULL) } else {# Reset the selected country when any specific region is chosenselected_country(NULL) } })
Reactives
What Reactives Do
Reactives are functions that return a value. You can assign them to an object and you can refer to reactives elsewhere in your app.
You can see them as backpacks 🎒 that carry values and expressions around that you can open whenever you want.
Types of Reactive Functions
When you go hiking, you pick a backpack that fits your needs. There are different types of backpacks that are fit to carry different things.
That’s similar with a reactive. They carry different things:
reactive(): takes an expression
reactiveVal(): takes a single value
reactiveValues(): takes a list of values
The reactive() Function
The reactive() Function
You can see reactive() as a very fancy full-size backpack 🎒🌟.
It can take multiple inputs, manipulate them and return something simple (a value) or complex (a plot, a table). It can even take other reactives (other backpacks) as input!
You can see reactiveValues() as some kind of reactive mini-database. You can use it to store multiple values, retrieve them in different places, and update them.
And since it’s reactive, you can use it to trigger other parts of your app when one of its values changes.
Line Chart App
Example from this Week
Server with reactive() Functions
# Download data from FRED with reactive function. # Only updates when user selects new indicator fred_indicator <-reactive({fredr(series_id = input$indicator,observation_start = start_date,observation_end = end_date) })# Filter data according to chosen years # Only updates when user selects new data range fred_data <-reactive({fred_indicator() |>filter(between(date, input$range[1],input$range[2])) })
Key Concepts
Focused Reactivity:
Each reactive() function isolates updates to specific user inputs.
This avoids redundant processing and enhances app performance.
Chained Dependencies:
reactive() functions can depend on each other.
Changes in one reactive output can trigger updates in another, maintaining efficient data flow.
Selective Execution:
Reduces the computational workload by recalculating only necessary parts of the data pipeline.
Reactivity is triggered only in response to relevant user interactions.
UI Code
ui <-fluidPage(# Application titletitlePanel("FRED Data App"),fluidRow(# 12 columns on one row: this panel will take 1/3 of itcolumn(4, wellPanel(selectInput("indicator", "Indicator:", vars) ),helpText("Select an indicator, choose a date range and view the trend. The grey bars represent economic recessions. The data for this app comes from the St. Louis Fed's FRED database. The consumer confidence, business confidence and lead composite indicators are OECD data downloaded through FRED.") ), # Remaining 2/3 occupied by plotcolumn(8,plotOutput("lineChart"), sliderInput("range","",min = start_date,max = end_date, value =c(start_date, end_date), width ="100%" ) ) ))
Setup (Global) Code
# Load packageslibrary(shiny)library(fredr)library(dplyr)library(ggplot2)# Set Fred API key fredr_set_key("YOUR FRED API KEY") # Assign FRED series to objectscci <-"CSCICP03USM665S"# consumer confidencebci <-"BSCICP03USM665S"# business confidencecli <-"USALOLITONOSTSAM"# composite lead indicatorunemp_rate <-"UNRATE"# unemployment rategrowth <-"A191RL1Q225SBEA"# growth rate# set start and end datestart_date <-as.Date("1970-01-01")end_date <-as.Date(Sys.Date())# Create list of named values for the input selectionvars <-c("Consumer Confidence"= cci, "Business Confidence"= bci, "Composite Indicator"= cli, "Unemployment Rate"= unemp_rate,"Growth Rate"= growth)# Load helper scriptsource("helper.R") # scroll down, code pasted below
Your Turn!
Do the prework, getting set up with fredr and other relevant packages
Create a NEW project folder
Save your helper script in a subfolder
Start on your app.R file
Extend the app by incorporating new indicators
10:00
Challenge 1–Observers
Notification Management:
Implement observeEvent() using showNotification() to show notifications when indicators are selected.
Action Logging:
Log changes to the indicator or date range to the R console with timestamps.
Dynamic UI Updates:
Use observe() to adjust the UI elements dynamically based on the indicator selected.
Challenge 2–Reactives
Implement a counter using reactiveValues() that increments each time the indicator input changes.
Display the counter value in the UI using textOutput().
Use observeEvent() to increment the counter reactively.
Create UI controls for selecting plot line type and color.
Use reactiveValues() to store the current plot style settings.
Update the plot rendering logic to use these settings dynamically.
Update our app to include vertical lines on the plot that show “black swan” events
Use check boxes to allow users to select which events to display
Use reactiveValues() to store the selected events, then plot