R Shiny: Use reactiveValues() with data.table assign-by-reference

Viewed 1564

I have a shiny app in which more than one reactive component uses the same result from a function that is slow to calculate. To avoid calculating the slow function more than once, I can use reactiveValues() to recalculate the function when its inputs change, and make the result available to all reactive components that require it.

But, if the reactiveValues object is a data.table, and I update it using :=, shiny does not detect the change, and the outputs that rely on it do not get updated.

Is there any way to use data.table assign by reference either with reactiveValues or another way that avoids recalculating the function multiple times.

Here is a reproducible example using data.table assign-by-reference in which output$result2 fails to get updated when the input changes:

library(shiny)
library(data.table)
library(plotly)

ui = fluidPage(
  sidebarLayout(
    sidebarPanel(
      sliderInput('x1', 'x1', min=0, max=2, value=1, step=0.1)
    ),
    mainPanel(
      plotlyOutput('result1'),
      verbatimTextOutput('result2')
    )
  )
)

server = function(input, output) {
  values <- reactiveValues()
  values$dt = data.table(v1 = 1:100, v2 = 1)

  slow.func = function(my.dt, x) {
    Sys.sleep(2)
    my.dt[, v2 := v1^x]
  }

  output$result1 = renderPlotly({
    values$dt = slow.func(values$dt, input$x1)
    plot_ly(values$dt) %>%
      add_lines(x = ~v1, y = ~v2)
  })      
  output$result2 = renderText({
    paste('Final value =', values$dt[.N, v2])
  })  
}
shinyApp(ui = ui, server = server)

For comparison, here is a version of the server function using standard assignment of data.frames, which does perform as expected:

server = function(input, output) {
  values <- reactiveValues()
  values$dt = data.frame(v1 = 1:100, v2 = 1)

  slow.func = function(my.dt, x) {
    my.dt$v2 = my.dt$v1^x
    Sys.sleep(2)
    my.dt
  }

  output$result1 = renderPlotly({
    values$dt = slow.func(values$dt, input$x1)
    plot_ly(values$dt) %>%
      add_lines(x = ~v1, y = ~v2)
  })

  output$result2 = renderText({
    paste('Final value =', values$dt[100,]$v2)
  })  
}
1 Answers
Related