Posting this in case it helps others like me who are new to Shiny. I was working on a Shiny application with the following requirements:

  • When the application loads, pull source data from an API.
  • Automatically refresh the data every x seconds or minutes thereafter.
  • Also allow the user to refresh data manually via an actionButton.

I was able to accomplish most of what I needed using these examples:

The methods I used are shown in the example code below. To keep things simple, I’ve used a call to Sys.time() to simulate an API call that retrieves continuously updated data. In the example, the timer is set to refresh the data every 5 seconds.

library(shiny)
library(lubridate)

ui <- fluidPage(
        actionButton("refreshData","Refresh data"),
        h3("Current seconds. Will increment by five unless manually refreshed."),
        h3(textOutput("currentTime"))
)

server <- function(input, output) {
        
        # initialize reactivevalues
        myReactiveData  <- reactiveValues(myAPIRequest = NULL)
        
        # create a timer
        autoInvalidate  <- reactiveTimer(5000)
        
        observeEvent(
                # events we're looking for
                eventExpr = {
                        input$refreshData # button is pressed
                        autoInvalidate() # timer runs down
                },
                # code to run when an event is observed
                handlerExpr = {
                        myAPIRequest <- Sys.time() # put API call here
                        
                        # do some data processing
                        processedResults <- round(second(myAPIRequest))
                        
                        # assign processed data to reactivevalues object
                        myReactiveData$dataToDisplay <- processedResults
                },
                # make handlerExpr run once when app first starts
                ignoreNULL = FALSE
        )
        
        output$currentTime <- renderText(
                myReactiveData$dataToDisplay
        )
}

shinyApp(ui, server)

This partially met the requirements, but there was a problem: manually refreshing the data did not affect the timer. If a user manually refreshed the data 3 seconds after initially loading the app, I wanted the timer to reset and wait 5 more seconds before the next automatic refresh. This wasn’t happening; the automatic refresh stuck to its original schedule and refreshed every 5 seconds from when the application originally loaded, regardless of user input.

I eventually found this Stack Overflow article, the key insight from which was the idea of making the timer function itself a reactivevalues object, like so:

autoInvalidate  <- reactiveValues(timer = reactiveTimer(5000))

In combination with that, I used a second observeEvent to look for the manual refresh and reset the timer. The complete updated code looks like this:

library(shiny)
library(lubridate)

ui <- fluidPage(
        actionButton("refreshData","Refresh data"),
        h3("Current seconds. Will increment by five unless manually refreshed."),
        h3(textOutput("currentTime"))
)

server <- function(input, output) {
        
        # initialize reactivevalues
        myReactiveData  <- reactiveValues(myAPIRequest = NULL)
        autoInvalidate  <- reactiveValues(timer = reactiveTimer(5000))
        
        observeEvent(
                eventExpr = {
                        input$refreshData
                },
                handlerExpr = {
                        # reset the timer on button press
                        autoInvalidate$timer <- reactiveTimer(5000)
                }
        )
        
        observeEvent(
                # events we're looking for
                eventExpr = {
                        input$refreshData # button is pressed
                        autoInvalidate$timer() # timer runs down
                },
                # code to run when an event is observed
                handlerExpr = {
                        myAPIRequest <- Sys.time() # put API call here

                        # do some data processing
                        processedResults <- round(second(myAPIRequest))
                        
                        # assign processed data to reactivevalues object
                        myReactiveData$dataToDisplay <- processedResults
                },
                # make handlerExpr run once when app first starts
                ignoreNULL = FALSE
        )
        
        output$currentTime <- renderText(
                myReactiveData$dataToDisplay
        )
}

shinyApp(ui, server)

So that’s it. In summary, you can do this in Shiny by using multiple observeEvent statements and assigning reactiveTimer to a reactivevalues object. I don’t know if this is the best way to accomplish this, but it worked for me.