reactiveValues vs reactive and eventReactive: a general question

I am late to the party, but even though I discovered Shiny about 4 years ago and have developed a few apps since then, I just recently watched the seminal talk from Joe Cheng at the Shiny Dev Conference in 2016. I must say that it really opened my eyes to many truths. The second and third takeaways given by him during the talk were:

  • Prefer using reactive expressions to model calculations over using observers to set reactive variables
  • Seriously, prefer using reactive expressions to model calculations over using observers to set reactive variables

This leads me to the following questions to all Shiny experts here. Is the reactiveValues() function completely useless? What I mean is can all reactivity needs for assigning values in a shiny app be satisfied with the use of reactive() and eventReactive() exclusively? Or is reactiveValues() inside observers similar to for loops in R? Use it only when there is no other option?

1 Like

Hi,

Consider the example below:

shinyApp(
  ui = fluidPage(
    numericInput("n", "n", 1),
    actionButton("go", "Click me to see the plot!"),
    plotOutput("plot")
  ),
  server = function(input, output, session) {
    
    output$plot <- renderPlot({
      if (input$go > 0) plot(head(cars, input$n))
      # or if (input$go) plot(head(cars, input$n))
    })
    
  }
)

Assume now that you are not allowed to use an actionButton but you want to show the plot 3 seconds after the shiny app started.

reactiveValues and reactiveVal are super useful to handle events such as setting a counter that can control whether to show outputs and when.

Below is the reactiveValues version but the reactiveVal would also do the job:

library(shiny)
library(shinyWidgets)

shinyApp(
  ui = fluidPage(
    numericInput("n", "n", 1),
    plotOutput("plot")
  ),
  server = function(input, output, session) {
    
    counter <- reactiveValues(value = 0)
    
    observe({
      counter$value <- isolate(counter$value) + 1
      invalidateLater(1000, session)
      if (counter$value == 3) {
        sendSweetAlert(
          session = session,
          title = "Success !!",
          text = "Now you can see the plot.",
          type = "success"
        )
      }
    })
    
    output$plot <- renderPlot({
      if (counter$value >= 3) plot(head(cars, input$n))
    })
    
  }
)

Don't forget the isolate in counter$value <- isolate(counter$value) + 1 to avoid updating the counter infinitely and crash your R session.

Here an example with more complex events: Shiny Contest Submission: Gotta Catch' Em (Almost) All. There are several reactiveValues examples in the pokeFight module.

2 Likes

@DGranjon Thank you very much for the response. I also remember seeing your Pokemon app as a retweet from Mara I believe.

I really appreciate that you provided an example; however, my question is more philosophical (for lack of a better word). I am just trying to find adequate answers/explanations to these questions.

Thank you again.

This question reminds me of a StackOverflow post I stumbled across a while back when I had a similar question. I think the answers here might be helpful! The 'eagerness' of observe() vs. the 'laziness' of reactive() is worth considering when crafting an app. https://stackoverflow.com/questions/39436713/r-shiny-reactivevalues-vs-reactive

In general, I agree with Dean Attali's answer in the post and generally prefer to use reactiveValues mainly for values that I might change in more than one place or for holding a 'state', such as a counter or flag. In my own work, I only use observe() with reactiveValues in my apps when trying to make some interesting UI interactivity, especially in conjunction with shinyjs.

I hope this helps answer your question!

3 Likes

Couple questions in here that I'll give my thoughts on.

Is reactiveValues function completely useless?

  • No. inputs and clientData area actually reactiveValues objects. They are helpful when sets (with possibly unknown names) of reactive information is necessary.
  • Of the objects that I create, they are 95% reactive objects.

Can all reactivity needs for assigning values in a shiny app be satistfied using reactive() and eventReactive()?

  • No. That being said, a lot of user input handling will only be using reactive() and eventReactive().
  • Example: Let's say you want to make a "deduplication" reactive. "It should react to an expression, but it should not update the value unless it's a new value." This would require a reactive expression, an observer, and a reactiveVal.
dedupe <- function(expr) {
  r <- reactive(expr)
  deduped <- reactiveVal(NULL)
  observe({
    deduped(r())
  })
  return(deduped)
}

Is reactiveValues() bad code smell?

  • No. I think of reactiveValues() as a reactive list(), rather than a reactive object. Since it is a list, there are a few extra methods that work with a reactiveValues() list object. Example: var <- reactiveValues(); r <- reactive()
    • names(var): If this was done with a reactive, it'd have to be: names(r()), assuming r produces a list object.
    • as.list(var): will produce a list object with all key value pairs.

tldr;

  • Use reactiveValues if you'd like a reactive() list.
  • 95% of my code is reactive() or observeEvent()
  • Use observe() only if side effects are necessary.
2 Likes

Thank you very much for the detailed response. It is really enlightening.

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.