Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Control execution flow following the update of several reactive dependencies

Tags:

r

shiny

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:

  • Counting events, as suggested by @jdharrison, can be a good hack but will not work here because if the loaded value with updateSelectInput() is the same as it used to be, the value is not sent back.
  • Waiting a bit, with something like 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.
like image 875
Antoine Lizée Avatar asked Nov 27 '14 01:11

Antoine Lizée


1 Answers

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.

like image 70
Antoine Lizée Avatar answered Oct 27 '22 10:10

Antoine Lizée