Is it possible to force shiny to render an output *immediately*?

1,911 views
Skip to first unread message

Dean Attali

unread,
May 11, 2017, 2:43:03 PM5/11/17
to Shiny - Web Framework for R
Consider this code:

library(shiny)

ui <- fluidPage(
  actionButton("btn", "Click"),
  uiOutput("out")
)

server <- function(input, output) {
  values <- reactiveValues(foo = NULL)
  
  observeEvent(input$btn, {
    values$foo <- "bar"
       Sys.sleep(1)
  })
  
  output$out <- renderUI({
    values$foo
  })
}

shinyApp(ui = ui, server = server)


Is there a way to tell shiny to update the text output immediately when the reactive value is assigned instead of waiting for the flush?

Joe Cheng

unread,
May 11, 2017, 5:54:52 PM5/11/17
to Dean Attali, Shiny - Web Framework for R
No. But it would make sense to add this as an outputOption(). It's not completely trivial, since the JS side makes some assumptions that all the values will be received together, but we should take the time to do it.

--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/db6c0683-25fd-4818-8212-832db1d56f8d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Joe Cheng

unread,
May 11, 2017, 6:19:43 PM5/11/17
to Dean Attali, Shiny - Web Framework for R
Can I trouble you to file a bug, Dean? I thought we had one, but now I can't find any.

Dean Attali

unread,
May 11, 2017, 6:29:06 PM5/11/17
to Shiny - Web Framework for R, daat...@gmail.com
Opening issues for shiny is never something I shy away from , I'll make sure you always have enough work :)

Andrew Sali

unread,
May 14, 2017, 2:19:40 PM5/14/17
to Shiny - Web Framework for R, daat...@gmail.com
@Dean: A possible solution to your example would be to leverage on `invalidateLater` (with a minimal delay time) to push the expensive code into the next flush after the assignment is made (thus the client receives the value update in time). I put together a small example that requires a bit of code rearranging to work, but ultimately seems to achieve what you are looking for ;)

library(shiny)

ui <- fluidPage(
  actionButton("btn", "Click"),
  uiOutput("out")
)

## A function that returns a reactiveVal that invalidates in the next flush AFTER expr_q has invalidated
flush_wait <- function(expr_q, env=parent.frame()) {
  transfer_reactive <- reactiveVal()
  # add transfer 
  shiny::observeEvent(expr_q,
                      {
                        n_run <- 0
                        o <- observe({
                          invalidateLater(1)
                          if (n_run==1) {
                            transfer_reactive(runif(1))
                          }
                          if (n_run > 1) {
                            o$destroy()
                          }
                          n_run <<- n_run+1
                        });
                      },event.env=env,event.quoted=TRUE)
  
  return(transfer_reactive)
}

server <- function(input, output,session) {
  values <- reactiveValues(foo = NULL)
  
  # return a reactiveVal that invalidates in the next flush AFTER values$foo has been assigned
  invalidate_after_assigned <- flush_wait(
    quote({
      req(input$btn);
      values$foo <- paste0("Button click #",input$btn)
    })
  )
  
  observe({
    req(invalidate_after_assigned())
    Sys.sleep(1)
    showModal(modalDialog("Long observer finished running"))
  })
  
  output$out <- renderUI({
    values$foo
  })
}

shinyApp(ui = ui, server = server)

Cheers,

Andras
Reply all
Reply to author
Forward
0 new messages