This is a straightforward yet advanced problem. Here is a walkthrough:
I have three inputs i_1, i_2, i_3, that lead to three reactive values that are used in different functions:
I1 <- reactive({input$i_1})
I2 <- reactive({input$i_2})
I3 <- reactive({input$i_3})
Specifically, these three values are used to plot a graph as a reactive endpoint:
output$plot <- renderPlot {(
plot( f(I1(), I2(), I3()) )
)}
The shiny magic works out and everything works fine, until I want to load some data and modify the three i_X values:
observe {(
input$b_Load #Load button
[...]
updateSelectInput(session,"i_1", value = 4)
updateSelectInput(session,"i_2", value = 3)
updateSelectInput(session,"i_3", value = 5)
...
)}
The update itself works fine, and the three new values for the variables are sent to the client.
The problem: The values are then updated and sent back one by one to the server, which recomputes the graph each time unnecessarily. I would like to update the graph only once, when all the values are updated. Is it possible?
I tried to wait for an onFlushed event:
values <- reactiveValues(loadingProfile = FALSE)
session$onFlushed(function() {
values$loadingProfile <- FALSE
cat("##### DEBUG: loading Profile OFF ##############\n", file = stderr())
}, once = FALSE)
output$plot <- renderPlot {(
if (!values$loadingProfile) {
plot( f(I1(), I2(), I3()) )
}
)}
observe {(
input$b_Load #Load button
values$loadingProfile <- TRUE #Toggle on the watching value
cat("##### DEBUG: loading Profile ON ##############\n", file = stderr())
[...]
updateSelectInput(session,"i_1", value = 4)
updateSelectInput(session,"i_2", value = 3)
updateSelectInput(session,"i_3", value = 5)
...
)}
but it doesn't work because the session is flushed each time one of the three updated versions is sent back to the server... (I checked on the console after activating the trace)
What will not work properly:
updateSelectInput()
is the same as it used to be, the value is not sent back.invalidateLater(2000, session)
kinda works (I tried it) but is not robust. It is not possible to specify a time that will always catch the different exchanges without overshooting and thus pausing execution for too long.This question doesn't have an answer per se from what I know of the current state of Shiny. Nevertheless, using the awesome reactive()
expressions, I've been able to refactor the flow of values and update the graph only once. In deed the
output$plot <- renderPlot {(
plot( f(User()) )
)}
observe {(
input$b_Load #Load button
updateSelectInput(session,"i_1", value = 4)
updateSelectInput(session,"i_2", value = 3)
updateSelectInput(session,"i_3", value = 5)
...
)}
User <- reactive({
object <- list()
object$i1 <- I1()
object$i2 <- I2()
object$i3 <- I3()
)}
+ a small rewrite of f()
.
Here, the User()
reactive expression plays the role of the buffer. It will wait for all the previous changes to be pushed before sending its own value to f()
.
I have shared my full Shiny project on Github, for more information.
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