shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Use req() in checkFunction ReactivePoll

Open HugoGit39 opened this issue 5 months ago • 5 comments

Hi

So I have the current workflow:

A shiny app where a user signs in via Firebase. Once signed it, I have an observeEvent that observes the sign in where a reactivevalue changes to TRUE value. Once this happens I want a reactivePoll starts running to load a dataframe from a server, which needs to be refreshed based on the day. When I add req() to checkFunction it errors, however when I add it to valueFunction it does work, however reactivePoll starts running once a user opens the app without signing-in, which I do not want.

I have a simplified code with a button which is a metphore for signing in and reactivePoll check every 30 seconds for the minute so the data changes once the button is clicked

library(shiny)

# Function to generate random data
generate_data <- function() {
  data <- data.frame(
    x = rnorm(10),
    y = rnorm(10)
  )
  return(data)
}

# Define UI
ui <- fluidPage(
  titlePanel("ReactivePoll Example"),
  mainPanel(
    actionButton("btn", "Click Me!"),
    uiOutput('uioutput')
  )
)

# Define server logic
server <- function(input, output, session) {
  
  data_poll <- reactiveVal(NULL)
  ready <- reactiveVal(FALSE)
  
  # Observing the button click event
  observeEvent(input$btn, {
    
    ready(TRUE)
    
  })
    
    # Function to poll for updated data every 30 seconds
    data_poll <- reactivePoll(
      intervalMillis = 30000,
      session = session,
      checkFunc = function() {
        # Return a timestamp to trigger polling

        current_minute <- format(Sys.time(), "%M")
        print(current_minute)
        
      },
      valueFunc = function() {
        # Generate new data
        req(ready())
        generate_data()
        
      }
    )
  
  
  output$uioutput = renderUI({
    if (!is.null(data_poll()) && nrow(data_poll()) > 0) {
      plotOutput("scatterPlot")
    }
  })
  
  # Render the scatter plot
  output$scatterPlot <- renderPlot({
    data <- data_poll()
    plot(data$x, data$y, main = "Scatter Plot", xlab = "X", ylab = "Y")
  })
  
}

# Run the application
shinyApp(ui, server)





HugoGit39 avatar Jan 22 '24 11:01 HugoGit39

Hi @HugoGit39, thanks for the question! The main issue is that req() throws an error that's normally handled gracefully by observers and reactives but that checkFunc isn't designed to handle. You can read the reactive value in checkFunc, though, so this pattern works:

  USER_LOGGED_IN <- reactiveVal(FALSE)

  observeEvent(input$login, {
    message("User has logged in.")
    # Pretend that the user has now logged in
    USER_LOGGED_IN(TRUE)
  })

  # Function to poll for updated data every 30 seconds
  data_poll <- reactivePoll(
    intervalMillis = 5000,
    session = session,
    checkFunc = function() {
      if (!USER_LOGGED_IN()) {
        message("Not logged in yet.")
        return()
      }

      # Return a timestamp to trigger polling
      message("Data check: ", format(Sys.time(), "%H:%M:%S"))
      format(Sys.time(), "%H:%M")
    },
    valueFunc = function() {
      # Generate new data
      req(USER_LOGGED_IN())
      generate_data()
    }
  )

I've put this modification in a small shinylive app from your example.

gadenbuie avatar Jan 22 '24 14:01 gadenbuie

@gadenbuie Thx!. But the checkFunc is running already from the start. Isnt there a way to start the checkFunc (or the reactivePoll function) when the user logs in?

HugoGit39 avatar Jan 22 '24 15:01 HugoGit39

@gadenbuie Thx!. But the checkFunc is running already from the start. Isnt there a way to start the checkFunc (or the reactivePoll function) when the user logs in?

No, the way that reactivePoll() works is that checkFunc should be a cheap and quick function that can be called repeatedly. Whenever its value changes, it gets a new value by calling valueFunc.

The idea in my updated example is to have checkFunc return immediately when the user isn't yet logged in. Because that's only checking an R object, it'll resolve immediately. So it's okay that it's running before the value of data_poll() is actually used.

gadenbuie avatar Jan 22 '24 18:01 gadenbuie

Right, but it its still a nice feature to have reactivePoll start running after a certain period right? Does checkFunc takes a lot of computing power on the server-side?

HugoGit39 avatar Jan 22 '24 21:01 HugoGit39

Right, but it its still a nice feature to have reactivePoll start running after a certain period right? Does checkFunc takes a lot of computing power on the server-side?

No, the code you use for checkFunc should be as quick and simple as possible. The idea is that checkFunc should run extremely quickly and be okay for Shiny to run more often than necessary without slowing down your app.

gadenbuie avatar Jan 22 '24 21:01 gadenbuie