Updating Reactive Value Breaks invalidateLater - r

In following shiny application:
When click the button, it prints to console once per second.
library(shiny)
library(rlang)
ui <- fluidPage(
textOutput("text"),
actionButton("button","Click to Start")
)
server <- function(input, output, session) {
myVal <- reactiveVal(0)
startCount <- reactiveVal(FALSE)
observeEvent(input$button,{
startCount(TRUE)
})
observe({
req(startCount())
req(myVal() < 5)
invalidateLater(1000)
newVal <-myVal() + 1
# myVal(newVal)
print(myVal())
})
output$text <- renderText(myVal())
}
shinyApp(ui, server)
By removing comment # myVal(newVal), it prints 1 to 5 in the console, and displays 5 in the UI. But there are two issues:
invalidateLater stops working, it doesn't wait for one second any more.
The UI jumps to 5 instantly, instead of showing 1,2,3,4,5 with one second interval.
What should I do to make it work as intended?

You'll need isolate to avoid recursively triggering the observer:
library(shiny)
library(rlang)
ui <- fluidPage(
textOutput("text"),
actionButton("button","Click to Start")
)
server <- function(input, output, session) {
myVal <- reactiveVal(0)
startCount <- reactiveVal(FALSE)
observeEvent(input$button,{
startCount(TRUE)
})
observe({
req(startCount())
req(isolate(myVal()) < 5)
invalidateLater(1000)
isolate(myVal(myVal() + 1))
print(myVal())
})
output$text <- renderText(myVal())
}
shinyApp(ui, server)

Related

R Shiny Debounce with reactiveValues and observeEvent / R input debounce

I am currently trying in vain to use the debounce function in Shiny to delay my input a bit. The goal is to have the renderText not fire every few milliseconds, but only after 2 second intervals.
I tried to implement the following solution. Thereby I absolutely need the reactiveValues and observeEvent functions. Other solutions here never take this combination into account and I am currently stuck. My example code is shortened. In reality the variable name1$data is still used by different functions and the RenderText accesses different variables.
if (interactive()) {
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactiveValues()
observeEvent(input$IText1, {Name1$data <- input$IText1})
#Solutions on stackoverflow
#Just causes errors for me
#Name1$t <- debounce(Name1$data, 2000)
output$OName <- renderText({
Name1$data
})
}
shinyApp(ui, server)
}
Thank you very much for any hint!
Normally we debounce reactive conductors (reactive({......})):
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactive({
input$IText1
})
Name1_d <- debounce(Name1, 2000)
output$OName <- renderText({
Name1_d()
})
}
shinyApp(ui, server)
EDIT
Or you need
server <- function(input, output, session) {
Name1 <- reactiveValues()
observe({
invalidateLater(2000, session)
Name1$data <- isolate(input$IText1)
})
output$OName <- renderText({
Name1$data
})
}

Shiny - reactive not evaluating directly on textarea change?

I'm creating a Shiny app where user input is parsed and then used to create specific plots. These plots should only be created when the user pushes a corresponding button. The text however should be parsed immediatly when user input changes.
Simplified example:
library(shiny)
ui <- fluidPage(
textAreaInput("txt", "Parse this data:"),
actionButton("go", "Do something with data"),
textOutput("out")
)
server <- function(input, output, session) {
data <- reactive({
message("Parsing data...")
toupper(input$txt)
})
observeEvent(input$go, {
output$out <- data
})
}
shinyApp(ui, server)
The "Parsing data..." message initially only executes when pushing the button, not when changing the input of the textAreaInput.
On top of that, after pushing the button once, the input is parsed immediatly when changed but the output is updated immediatly as well. This also shouldn't happen, it should only change when the button is pressed again.
EDIT
With YBS's answer I found a solution, and using isolate() I don't even need an extra variable:
server <- function(input, output, session) {
data <- reactiveVal()
observeEvent(input$txt, {
message("Parsing data...")
data(toupper(input$txt))
})
observeEvent(input$go, {
output$out <- renderText({ isolate( data() ) })
})
}
Try this
library(shiny)
ui <- fluidPage(
textAreaInput("txt", "Parse this data:"),
actionButton("go", "Do something with data"),
textOutput("out")
)
server <- function(input, output, session) {
rv <- reactiveVal()
data <- reactive({
message("Parsing data...")
toupper(input$txt)
})
observeEvent(input$go, {
rv(data())
})
output$out <- renderText({rv()})
}
shinyApp(ui, server)

R Shiny: How to display every iteration of a loop on browser? The loop should be triggered on button click

Have create a Shiny App that runs a FOR loop. Every iteration of the loop is displayed on the screen. Have used invalidatelater and isolate functions to do it. The working code as below.
ui <- fluidPage(
textOutput("loc")
)
server <- function(input, output, session) {
rv <- reactiveValues(n = 0)
observe({
invalidateLater(300, session)
isolate({
if (rv$n >= 100) {
return()
} else {
rv$n <- rv$n + 1
}
})
})
output$loc <- renderText({rv$n})
}
shinyApp(ui, server)
The FOR loop runs instantly. I want to trigger the FOR loop only after the button is clicked. Also want to put in the value of 'millis' for invalidateLater from a textInput. I used the below code, but it DID NOT WORK. Basically the FOR loop is not getting triggered.
ui <- fluidPage(
textInput("Test", "Test Input")
,actionButton("Generate_CCR", "Generate CCR")
,textOutput("test_out")
,textOutput("loc")
)
server <- function(input, output, session) {
test1 <- eventReactive(input$Generate_CCR,{t <- input$Test})
rv <- reactiveValues(n = 0)
eventReactive(input$Generate_CCR,{
invalidateLater(test1(), session) # Re-execute this reactive expression immediately after it finishes (though you can also specify a longer time: e.g. 1000 means re-execute after 1000 milliseconds have passed since it was last executed)
isolate({
if (rv$n >= 100) {
return()
} else {
rv$n <- rv$n + 1
}
})
})
output$test_out <- renderText(test1())
output$loc <- renderText({rv$n})
}
shinyApp(ui, server)
I cannot figure out the issue. Can someone help?
I think the easiest thing to do would just be to add a reactive value to track the state of your counter. For example
ui <- fluidPage(
textInput("Test", "Test Input")
,actionButton("Generate_CCR", "Generate CCR")
,textOutput("test_out")
,textOutput("loc")
)
server <- function(input, output, session) {
test1 <- eventReactive(input$Generate_CCR, as.numeric(input$Test))
rv <- reactiveValues(n = 0, runLoop = FALSE)
observe({
if(rv$runLoop) invalidateLater(test1(), session) else return();
isolate({
if (rv$n >= 20) {
rv$runLoop <- FALSE
return()
} else {
rv$n <- rv$n + 1
}
})
})
observeEvent(input$Generate_CCR, {
rv$n <- 0
rv$runLoop <- TRUE
})
output$test_out <- renderText(test1())
output$loc <- renderText({rv$n})
}
shinyApp(ui, server)
Here we added runLoop to keep track of whether or not the loop should run. We also changed it so the button just resets the loop and a separate observer keeps track of the current iteration value.

Changing output after delay in R Shiny App

I'm trying to get an app which updates it output after a set amount of time (i.e. to make text fade away).
In the example code, I would want "Waiting" to display on the actionButton press, then delay for 5 seconds, then the text changes to "Finished".
For some reason the whole observeEvent executes at once, so the outcome is that there is the actionButton is pressed, then there is a 5 second delay with nothing displayed, then "Finished" displays.
Sorry I don't know how to better explain the issue really - but hoping someone can help. If possible I'd like to stick to just R here, without delving too much into javascript.
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
num(1)
})
observe({
if(!is.null(num())){
output$text <- renderText({
Sys.sleep(5)
"finished"
})
}
})
}
shinyApp(ui = ui, server = server)```
You can use the delay() function from shinyjs
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(),
textOutput("text"),
actionButton("press", label = "press")
)
server <- function(input, output) {
num <- reactiveVal()
observeEvent(input$press, {
output$text <- renderText("waiting")
delay(5000,
output$text <- renderText("finished"))
})
}
shinyApp(ui = ui, server = server)

How to make an operation uninterruptible in R shiny

In my shiny app I have a output which should update itself continuously. But whenever I execute a long-running calculation, the output is just paused. My question is: how to make the output runs continuously and uninterruptible?
Please see the short demo below:
The clock refreshes every one second, but if I click the button which runs for 5 seconds, the clock is paused.
library(shiny)
ui <- fluidPage(
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
observeEvent(input$button,{
Sys.sleep(5)
output$result <- renderText(runif(1))
})
}
shinyApp(ui, server)
I tried to use future and promises to make the long-running process runs asynchronously, but it doesn't work. Where is wrong? And is there a better way for achieving this purpose?
library(shiny)
library(future)
library(promises)
plan("multisession")
ui <- fluidPage(
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
process <- eventReactive(input$button,{
future({
Sys.sleep(5)
runif(1)
})
})
output$result <- renderText(process())
}
shinyApp(ui, server)
Any help is appreciated!
Thanks #Shree for pointing out the solution. After reading the response from Joe Cheng. It seems like the key is to:
Hide the async operation from Shiny by not having the promise be the last expression.
The problem is resolved by creating a reactive value and assign the promise to it in observeEvent as the side effect.
server <- function(input, output, session) {
output$time <- renderText({
invalidateLater(1000)
as.character(Sys.time())
})
process <- reactiveVal()
observeEvent(input$button,{
output$isbusy <- renderText("busy") # a simple busy indicator
future({
Sys.sleep(5)
runif(1)
}) %...>%
process()
# Hide the async operation from Shiny by not having the promise be the last expression
NULL # important
})
output$result <- renderText({
output$isbusy <- renderText("") # a simple busy indicator
process()
})
}
Your problem is that Sys.sleep() suspends the execution of R expression. You can use things like the delay() function from shinyjs
library(shiny)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), #You have to add this.
actionButton("button","Expensive calcualtion(takes 5 seconds)"),
tags$p("Current Time:"),
textOutput("time"),
tags$p("Result from clicking button:"),
textOutput("result")
)
server <- function(input, output, session) {
timer <- reactiveTimer(500)
current_time <- reactive({
timer()
as.character(Sys.time())
})
output$time <- renderText(current_time())
observeEvent(input$button,{
delay(5000, output$result <- renderText("result"))
})
}
shinyApp(ui, server)

Resources