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)
})
}
say you have defined a reactive variable table_updated
so you can increment it by one each time the slow function is done. Other values/plots will only need to observe table_updated
.
Actually the actionButton(see description section) does the same thing, every time it gets clicked, its value is incremented by 1.
values <- reactiveValues(table_updated = 0)
slow.func = function(my.dt, x) {
# do stuff
values$table_updated <- values$table_updated + 1
}
output$result2 = renderText({
values$table_updated
paste('Final value =', values$dt[100,]$v2)
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With