Observe Function in R Shiny - How to Implement a Reactive Observer
It's easy to get down the basics of R and Shiny, but the learning curve becomes significantly steeper after a point. Reactive values? Reactive events? Reactive observers? Let's face it - it's easy to get lost. We're here to help. Today you'll learn all about the <strong>Observe function</strong> in R Shiny - what it is, what it does, and how to use it in practice with two <strong>hands-on examples</strong>. We'll kick things off with a bit of theory, just so you can understand why <strong>reactive observers</strong> are useful. <blockquote>Do you need help with your enterprise Shiny app? <a href="https://appsilon.com/#contact" target="_blank" rel="noopener">Reach out to the Shiny experts</a>.</blockquote> Table of contents: <ul><li><a href="#theory">What is Observe Function in R Shiny / Reactive Observer</a></li><li><a href="#example-1">Example 1 - Basic Demonstration of the Observe Function in R Shiny</a></li><li><a href="#example-2">Example 2 - Using a Reactive Observer to Update Dropdown Choices</a></li><li><a href="#summary">Summary of a Reactive Observer in R Shiny</a></li></ul> <hr /> <h2 id="theory">What is Observe Function in R Shiny / Reactive Observer</h2> The expression passed into the <code>observe()</code> function is triggered every time one of the inputs changes. If you remember only a single sentence from this article, that should be the one. So, what makes the <code>observe()</code> function different from regular reactive expressions? Well, <code>observe()</code> yields no output and can't be used as an input to other reactive expressions. Reactive observers are only useful for their "side effects", such as I/O, triggering a pop-up, and so on. <blockquote>Is your R Shiny app ready for deployment? <a href="https://appsilon.com/how-to-share-r-shiny-apps/" target="_blank" rel="noopener">Here are 3 ways to share R Shiny apps</a>.</blockquote> As mentioned before, observers re-execute as soon as their dependencies change, making them use a concept known as <b>eager evaluation</b>. On the other end, reactive expressions are lazy-evaluated, meaning they have to be called by someone else to re-execute. You can check all the parameters the <code>observe()</code> function can accept in the <a href="https://shiny.rstudio.com/reference/shiny/latest/observe.html" target="_blank" rel="noopener">official documentation</a>, but we won't go over that here. Instead, we'll dive into two hands-on examples. <h2 id="example-1">Example 1 - Basic Demonstration of the Observe Function in R Shiny</h2> To demonstrate how the Observe function in R Shiny works, we'll do something you'd never do in a real life. But there's a good reason why we're doing it. It perfectly demonstrates what the <code>observe()</code> function does, and how a reactive observer works. We already mentioned that the function is triggered every time one of the inputs changes. So, we can declare input and attach it to an observer to monitor what happens as we mess around with it. That's exactly what the code snippet below does. There are two UI elements - <code>sliderInput</code> and <code>textOutput</code>. Inside the <code>server()</code> function, we have attached a reactive observer to the <code>sliderInput</code>, and each time it changes, we update the text contents of the <code>textOutput</code>. There are easier and more intuitive ways to implement the same behavior, but you get the gist - each time the input changes we trigger some code. It's irrelevant which code we trigger, but in this case, the code just changes the text: <pre><code class="language-r">library(shiny) <br>ui <- fluidPage( sliderInput(inputId = "slider", label = "Select a number:", min = 1, max = 100, value = 10, step = 1), textOutput(outputId = "result") ) <br>server <- function(input, output) { observe({ input$slider output$result <- renderText({ input$slider }) }) } <br>shinyApp(ui = ui, server = server)</code></pre> Below you'll find a GIF demonstrating how the R Shiny app works: <img class="size-full wp-image-13432" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2ac9820581b8cdc6b149d_1.gif" alt="Image 1 - Basic example of an R Shiny reactive observer" width="422" height="196" /> Image 1 - Basic example of an R Shiny reactive observer That was easy, right? Next, we'll go over a slightly more complicated example. <h2 id="example-2">Example 2 - Using a Reactive Observer to Update Dropdown Choices</h2> All dashboards have dropdown menus, or select inputs, as R Shiny calls them. The premise is simple - you select a value, and then charts/tables on a dashboard are re-drawn. But how can you introduce dependencies between dropdown menus? In other words, how can you base what's shown as options for the second dropdown menu based on the value selected from the first menu? That's where the Observe function in R Shiny comes in. If you want to follow along, copy the following dataset and save it as <code>data.csv</code>: <pre><code class="language-csv">"x","y" "A","A1" "A","A2" "A","A3" "A","A4" "A","A5" "B","B1" "B","B2" "B","B3" "B","B4" "B","B5" "C","C1" "C","C2" "C","C3" "C","C4" "C","C5"</code></pre> The example Shiny dashboard below will read the dataset and declare two <code>selectInput</code>'s in the UI. The second depends on the first. For example, if the user selects "A" in the first dropdown menu, only options of "A1" to "A5" should be shown in the second menu. The same thing goes for the other letters. <blockquote>Need help from a Shiny consultant? See how <a href="https://appsilon.com/rstudio-certified-partner/" target="_blank" rel="noopener">RStudio (Posit) Certified Partners can help you and your team</a>.</blockquote> Inside the <code>server()</code> function, we'll use the <code>dplyr</code> package to filter out the records we don't need, and then use the <code>updateSelectInput()</code> function to "re-write" the choices for the second dropdown menu. That's it! Let's see the code: <pre><code class="language-r">library(shiny) library(dplyr) <br>dummy_data <- read.csv("data.csv") <br>ui <- fluidPage( selectInput(inputId = "col_x", label = "X:", choices = dummy_data$x, selected = dummy_data$x[1]), selectInput(inputId = "col_y", label = "Y:", choices = dummy_data$y, selected = dummy_data$y[1]) ) <br>server <- function(input, output, session) { observe({ y_vals <- dummy_data %>% filter(x == input$col_x) %>% select(y) updateSelectInput( session = session, inputId = "col_y", choices = y_vals, selected = head(y_vals, 1) ) }) } <br>shinyApp(ui = ui, server = server)</code></pre> Below is a GIF showing you how the app works: <img class="size-full wp-image-13434" src="https://webflow-prod-assets.s3.amazonaws.com/6525256482c9e9a06c7a9d3c%2F65b2abe9385f15aced6544c9_2.gif" alt="Image 2 - Updating dropdown menu options with the Observe function" width="494" height="334" /> Image 2 - Updating dropdown menu options with the Observe function Works like a charm. Let's make a short recap and go over the homework assignment. <hr /> <h2 id="summary">Summary of a Reactive Observer in R Shiny</h2> Today you've learned the basics of the Observe function in R Shiny (reactive observer) both through theory and practical examples. The moral of the story is as follows - use the <code>observe()</code> function every time you want your Shiny app to react to a change of the input. Common examples are conditionally rendered UI elements, such as options of a dropdown menu. For a homework assignment, we encourage you to explore the reactive observer further on your own. You could try working with radio buttons, and conditionally change the values of a subsequent group based on what the user previously selected. Give it a go, and make sure to share your results with us on Twitter - <a href="https://twitter.com/appsilon" target="_blank" rel="noopener">@appsilon</a>. We'd love to see what you come up with. <blockquote>Looking to migrate your RStudio workflow to cloud? <a href="https://appsilon.com/r-studio-cloud/" target="_blank" rel="noopener">Here's how you can get started for free</a>.</blockquote>