server <- function(input, output, session) {
output$greeting <- "Hello human"
}
shinyApp(ui, server)
#> Error: Unexpected character object for output$greeting
#> ℹ Did you forget to use a render function?Shiny Reactivity
Key Components of Shiny Reactivity:
Reactive Inputs:
These are user interface elements like sliders, text inputs, checkboxes, and dropdowns. When a user interacts with these elements, their values change, and Shiny recognizes these changes as triggers for reactivity.
Reactive Expressions (reactive(), eventReactive()):
These are R expressions that depend on reactive inputs or other reactive expressions. They are “lazy,” meaning they only re-execute when their dependencies change and when their value is needed by an output or another reactive expression.
- They are defined using the
reactive()function and - Are called like functions (e.g.,
my_reactive_expression()).
Reactive Outputs (render*() functions):
These are the components displayed in the user’s browser, such as plots, tables, or text. They are created using renderPlot(), renderTable(), renderText(), etc., and
- Automatically re-render when their underlying reactive expressions or inputs change.
Observers (observeEvent(), observe()):
These are used for side effects, such as updating UI elements or writing to a database, that don’t directly produce an output to be displayed. They execute whenever their dependencies change, similar to reactive expressions,
- but their primary purpose is to trigger actions rather than return a value.
Basic Reactivity
The ui is simple because every user gets the same HTML. The server is more complicated because every user needs to get an independent version of the app; when user A moves a slider, user B shouldn’t see their outputs change.
To achieve this independence, Shiny invokes your server() function each time a new session5 starts.
Inputs are read-only
- Try to modify an input inside the server function, you’ll get an error:
- If you really want to
updateNumericInput()is the way but we’ll learn that later inputis selective about who is allowed to read it. To read from an input, you must be in a reactive context created by a function likerenderText()orreactive(). (Try accessing outside of these contexts and see what happens)
Outputs
output, like input is a list-like object. But output is always used in concert with a render function.
The render function does two things:
- It sets up a special reactive context that automatically tracks what inputs the output uses.
- It converts the output of your R code into HTML suitable for display on a web page.
Like the input, the output is picky about how you use it. You’ll get an error if:
- You forget the render function.
- You attempt to read from an output.
server <- function(input, output, session) {
message("The greeting is ", output$greeting)
}
shinyApp(ui, server)
#> Error: Reading from shinyoutput object is not allowed.Reactive Programming
Run this example:
ui <- fluidPage(
textInput("name", "What's your name?"),
textOutput("greeting")
)
server <- function(input, output, session) {
output$greeting <- renderText({
paste0("Hello ", input$name, "!")
})
}Imperative vs declarative programming
This difference between commands and recipes is one of the key differences between two important styles of programming:
In imperative programming, you issue a specific command and it’s carried out immediately. This is the style of programming you’re used to in your analysis scripts: you command R to load your data, transform it, visualise it, and save the results to disk.
In declarative programming, you express higher-level goals or describe important constraints, and rely on someone else to decide how and/or when to translate that into action. This is the style of programming you use in Shiny.
With imperative code you say Make me a sandwich. With declarative code you say Ensure there is a sandwich in the refrigerator whenever I look inside of it. Imperative code is assertive; declarative code is passive-aggressive.