A few things I learned about shiny (and reactive programming)

One of the things I really like about shiny is that it has excellent documentation: the tutorial, articles and gallery go a long way in helping newcomers as well as intermediate programmers mastering the structure and features of shiny. Still, there are a few things I found lacking from the documentation but important to understand especially if your shiny app is going to be more than just a few lines of R code. I’m sharing them here hoping they can help others avoid some of the roundabouts I took as I learn my way in shiny.

Reactive dependencies are dynamic

What does this mean? It’s probably best illustrated with a somewhat contrived example. Consider the following two observers:

observe({
  if(input$a=="good"){
    print("good")
  } else {    
    print(input$b)
  }
})

observe({
  a <- input$a
  b <- input$b
  if(a=="good"){
    print("good")
  } else {
    print(b)
  }
})

Are they equivalent since the second is simply a reorganization of the first? The answer is no. The subtle difference between the two is that the second observer will always have dependencies on both input$a and input$b (so will re-run whenever one of them changes), vs the first observer will only depend on input$b when input$a != "good" (so when input$a == "good", it will only print “good” once no matter how many times you change input$b). The reason for this behavior is that the dependencies are built as the code is run (ie, dynamically), not based on how the observer (or any reactives) is initially defined. This may seem like a bug to some people, but it’s actually consistent with how R works in general, and can be a very useful feature once it’s well understood. I personally used this feature extensively in my R package shinyData to help separate the business logic from the UI.

Note: see this discussion post for a user’s question on this topic.

Reactives: order of execution

Here’s my understanding of the order of execution for reactives: when a user changes something in the browser, the browser sends the new input value to the server, which triggers a flush event and invalidates all the input’s dependents. Then all the reactive endpoints (like observers and outputs) are refreshed, which may also trigger a refresh of the reactive conductors (or reactive expressions defined with the reactive function). The part is actually well documented. However, things get trickier when you have calls to update inputs (like updateSelectInput). They will not execute until all the currently invalidated reactives finished executing, then the server sends messages back to the browser for all the update-input calls. If this results in any changes in input values, then another cycle of refreshing continues. So if you are not careful, it’s pretty easy to end up with a infinite loop if your update-input calls actually change some input values (not just the choices of a select input, for example).

After a flush event occurred, you can control which reactive endpoints are refreshed first by setting the “priority” argument (see ?observe). In my opinion this technique should only be used when absolutely necessary since the order of execution for reactives are in general unpredictable. Additionally, if a reactive is going to be invalidated as a result of an update-input call, then it is going to be refreshed after all the currently invalidated reactives finished refreshing no matter how high the priority you set it to be.

Use of isolate to prevent accidental dependencies

When you app gets more complex, it’s really important to prevent unintended dependencies for your reactives. I found the following coding pattern very helpful in showing what the dependents of a reactive are:

observe({
  ## part of code this reactive should take dependency on
  ...
  ...
  isolate({
    ## part of code this reactive should NOT take dependency on
    ...
    ...
  })
})

Conditional panel

The online article on dynamic UI explains that the condition of a conditional panel can use the result of a reactive expression in addition to input values. This provides a tremendous level of flexibility. So you can do things like:

# Partial example
## ui.R
conditionalPanel(condition="output.foo", ...)
## server.R
output$foo <- reactive({
  ...
})

However, what’s missing from the documentation is that in order to make it actually work, you need to add the following line to your server.R:

outputOptions(output, "foo", suspendWhenHidden=FALSE)

This is necessary because you’re unlikely to display the value of output$foo in your UI, and shiny by default suspends all output reactives when they are not displayed.

reactiveValues

reactiveValues is a powerful construct in shiny. Most of the common objects in R (like lists and data frames) have copy-on-modify semantics that behave somewhat in between reference classes and value classes as in languages like C or PHP. Basically, what copy-on-modify means is that when a list is assigned to another variable, that list is not copied immediately, but it is copied when some elements are modified with the new variable. This mechanism works well in most usages of R, but it can get quite frustrating if you try any type of object oriented programming with R, as you would normally expect objects like lists to have reference semantics, ie, a copy is never made unless you explicitly choose to do so. Fortunately, this is the case with reactiveValues, as can be seen in the following simple example:

library(shiny)
values <- reactiveValues(a = 1)
isolate(values$a)
## [1] 1
v1 <- values
v1$a <- 2
isolate(values$a)
## [1] 2

What this means is that you don’t have to worry about creating accidental copies of a reactiveValues object you created so the dependent reactives don’t get updated.

Note members of reactiveValues can be any valid R objects (they can even be other reactiveValues). How does shiny determine if a member is changed if the member is a complex object like a list? Below is a simple shiny app I put together for testing this. As you can see for yourself if you run the app, shiny is pretty smart about detecting the change. It looks “deeply” in the object, so a change in the number of elements or an element’s value will both be detected.

## ui.R
shinyUI(fluidPage(
  sidebarLayout(
    sidebarPanel(
      numericInput('aa','a$aa',value=1),
      numericInput('bbb','a$bb$bbb',value=1),
      actionButton('addList', "Add to list a"),
      br(), br(),
      numericInput('rr','r$rr',value=1),
      actionButton('addToR', "Add to r")
      ),
    mainPanel(
      h3('values'),
      verbatimTextOutput('a')
      )
    )
))

## server.R
library(shiny)
shinyServer(function(input, output) {
  values <- reactiveValues(a = list(aa=1, bb=list(bbb=1)), r=reactiveValues(rr=1))

  output$a <- renderPrint({
    list(a=values$a, r=reactiveValuesToList(values$r))
  })
  observe({
    input$aa
    isolate({  ## use isolate to avoid dependency on values$a
      values$a$aa <- input$aa  ## no need to use <<- because of reference semantics with reactiveValues
    })
  })
  observe({
    input$bbb
    isolate({
      values$a$bb$bbb <- input$bbb
    })
  })
  observe({
    input$rr
    isolate({
      values$r$rr <- input$rr
    })
  })
  observe({
    if(input$addList>0){  ## not run when being initialized
      isolate({
        values$a[[paste0('aa', length(values$a))]] <- 1
      })
    }
  })
  observe({
    if(input$addToR>0){  ## not run when being initialized
      isolate({
        values$r[[paste0('rr', length(reactiveValuesToList(values$r)))]] <- 1
      })
    }
  })
})

That’s probably enough for this post. If others find it useful, I might put together a follow-on post.

12 thoughts on “A few things I learned about shiny (and reactive programming)”

    1. thanks for a great post! your note about the required ‘outputOptions’ function was the answer to what I’ve been beating my head against the wall on for the last several hours!

      Like

  1. Thanks for the post, it’s probably the closest thing I found on the web on the problem I cannot wrap my head around yet. It’s a long shot but maybe any poster here has a clue.
    I load a dataset and the app waits (lines 4-6) until I assign it to ds (line 7). Only then, the drop down menu is filled with the names of the variable of the dataset, and the user (line 8-10) can chose one variable whose class must be transformed from numeric (its default) to character (line 11).
    I start with a selected=”None” because in general I do not know which is the name of the time variable whose class needs to be set to character.

    1 observe({
    2 file1 = input$dataset
    3
    4 if(is.null(input$dataset)){ #This tells the App to wait…
    5 return(NULL)
    6 }
    7 ds = as.data.frame(read_sas(file1$datapath)) # …until there is a dataset
    8 year = reactive({names(ds)})
    9 updateSelectInput(session, “time”, choices = c(“None”,year()), selected = “None”)
    10 timepos <- which(colnames(ds)==input$time)
    11 ds[,c(timepos)] <- as.character(ds[,c(timepos)])

    etcetc…

    Problem: shiny obviously re-runs the code every time an instance changes, so unfortunately the class snaps always back immediately to its default (numeric). Note that if I assumed I knew that the variable is VAR_YEAR and I set that as selected="VAR_YEAR" all works as I want.
    Any idea how to go around this?

    Like

  2. “The subtle difference between the two is that the second observer will always have dependencies on both input$a and input$b (so will re-run whenever one of them changes), vs the second observer will only depend on input$b when input$a != “good” (so when input$a == “good”, it will only print “good” once no matter how many times you change input$b).”

    is there something wrong there? they all talk about the second observer, but one is “always have dependencies on both”, the other is “only depend on input$b”.

    Like

Leave a comment