Ron Talbot Ron Talbot - 2 months ago 29
R Question

R Shiny: reactiveValues vs reactive

This question is related to this one. The two can generate the same functionality, but implementation is slightly different. One significant difference is that a

reactiveValue
is a container that can have several values, like
input$
. In shiny documentation functionality is usually implemented using
reactive()
, but in most cases I find
reactiveValues()
more convenient. Is there any catch here? Are there any other major differences between the two that I might not be aware off? Are these two code snippets equivalent?

See the same example code implemented using:


  1. a reactive expression:

    library(shiny)

    ui <- fluidPage(
    shiny::numericInput(inputId = 'n',label = 'n',value = 2),
    shiny::textOutput('nthValue'),
    shiny::textOutput('nthValueInv')
    )

    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

    server<-shinyServer(function(input, output, session) {
    currentFib <- reactive({ fib(as.numeric(input$n)) })
    output$nthValue <- renderText({ currentFib() })
    output$nthValueInv <- renderText({ 1 / currentFib() })
    })

    shinyApp(ui = ui, server = server)

  2. a reactive value:

    library(shiny)

    ui <- fluidPage(
    shiny::numericInput(inputId = 'n',label = 'n',value = 2),
    shiny::textOutput('nthValue'),
    shiny::textOutput('nthValueInv')
    )

    fib <- function(n) ifelse(n<3, 1, fib(n-1)+fib(n-2))

    server<-shinyServer(function(input, output, session) {
    myReactives <- reactiveValues()
    observe( myReactives$currentFib <- fib(as.numeric(input$n)) )
    output$nthValue <- renderText({ myReactives$currentFib })
    output$nthValueInv <- renderText({ 1 / myReactives$currentFib })
    })

    shinyApp(ui = ui, server = server)


Answer

There is a catch, though it won't come into play in your example.

The shiny developers designed reactive() to be lazy, meaning that the expression contained in it will only be executed when it is called by one of its dependents. When one of its reactive dependencies is changed, it clears its cache and notifies its dependents, but it is not itself executed until asked to by one of its dependents. (So if, say, its sole dependent is a textOutput() element on a hidden tab, it won't actually be executed if/until that tab is opened.)

observe(), on the other hand, is eager; the expression that it contains will be executed right away whenever one of its reactive dependencies is changed -- even if it's value is not needed by any of its dependents (and in fact even if has no dependents). Such eagerness is desirable when you're calling observe() for its side-effects, but it can be wasteful when you're only using it to pass on the return value of its contents to other reactive expressions or endpoints down the line.

Joe Cheng explains this distinction quite well in his 2016 Shiny Developer Conference presentation on "Effective reactive programming", available here. See especially the bit starting around 30:20 in the presentation's second hour. If you watch until 40:42 (blink and you'll miss it!) he briefly characterizes the behavior of the observe()/reactiveValue () combination that you like.

Comments