Delete/reset eventReactive value in shiny (R)

25 views Asked by At

I have a shiny app that uses plotly to display data after the data was loaded with an actionButton. In my full app, I have a lot of subsequent reactive observers, including other reactive values, that depend on the existence of the data being loaded. Ultimately, this data will be loaded from file and I want the user to be able to reset everything and completely start from scratch. However, I don't understand how to delete the reactive value or at least set it to NULL unless I store it within reactiveValues(). I tend to prefer using req() over if(!is.null()) in my observers and Dat() over vals$dat, if possible.

library(shiny)
library(shinyjs)
library(plotly)

df <- data.frame(t=seq(as.POSIXct("2024-01-01 00:00:00", tz='UTC'),
                       as.POSIXct("2024-01-02 00:00:00", tz='UTC'), by="1 hour"),
                 V1=sample(1:50,25, replace=T))

ui <-shinyUI(fluidPage(
  shinyjs::useShinyjs(),
  fluidRow(
      actionButton("getdat", "Get my Data"),
      actionButton("reset", "Reset"),
      ),
   fluidRow(
      plotlyOutput("plot"),
   )))

This does not work:

server<- function(input, output, session) {
    
  Dat<-eventReactive(input$getdat, {
    return(df)
  })
  
  #turn off button after data was loaded
  observeEvent(input$getdat,{
    shinyjs::disable("getdat")
    })
  
  output$plot <- renderPlotly({
    req(Dat())
    Dat() %>%  
      plot_ly() %>%
      add_trace(x= ~t, y = ~V1, type='scatter', mode='line', visible=TRUE) %>%
      layout(showlegend=FALSE)
  })
  
  observeEvent(input$reset,{
    Dat()<-NULL
    shinyjs::enable("getdat")
  })
  
}

shinyApp(ui,server)

This works:

server<- function(input, output, session) {
  
  vals<-reactiveValues(
    Dat=NULL
  )
   
  #load data and turn off button after data was loaded
  observeEvent(input$getdat,{
    vals$Dat<-df
    shinyjs::disable("getdat")
    })
  
  output$plot <- renderPlotly({
    if(!is.null(vals$Dat)){
      vals$Dat %>% 
      plot_ly() %>%
      add_trace(x= ~t, y = ~V1, type='scatter', mode='line', visible=TRUE) %>%
      layout(showlegend=FALSE)
    }
  })
  
  observeEvent(input$reset,{
    vals$Dat<-NULL
    shinyjs::enable("getdat")
  })
  
}

shinyApp(ui,server)

Edit:

I've implemented @Stéphane Laurent's solution and added some of the complexity that will be part of my full app. I guess I don't understand how to properly update a reactive container and the difference between eventReactive and observeEvent. I've been using eventReactive following the accepted answer to this question.

I know I could have included the update to the timestamps inside the updateData() function, and thereby include input$t0 as one of the events in eventReactive, but because I work with very large datasets, I'm trying to avoid having to recalculate everything every time one parameter is changed.

ui <-shinyUI(fluidPage(
  shinyjs::useShinyjs(),
  fluidRow(
    actionButton("getdat", "Get my Data"),
    actionButton("reset", "Reset"),
    textInput("bin", HTML("Average bin"), value = "1", placeholder = NULL),
    textInput("t0", HTML("Starttime"), 
              value = '2018/03/28 09:54:27',placeholder = NULL)
  ),
  fluidRow(
    plotlyOutput("plot")
  )))

updateData <- function(dat,bin) {
  dat<-as.data.frame(
    dat %>%
      group_by(t = round_date(t, unit=paste0(bin,' hours')))%>%
      summarize(V1 = median(V1))
  )
  return(dat)
}

updateStarttime <- function(dat,t0){
  timediff<-difftime(dat$t[1],as.POSIXct(t0, tz='UTC'), unit='secs')
  dat$t<-dat$t-seconds(timediff)
  return(dat)
}

server<- function(input, output, session) {
  
  rawDat <- reactiveVal()
  timeformat <- reactiveVal()
  
  observe(
    timeformat(grepl('[0-9]{4}/[0-9]{2}/[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}', input$t0))
  )
  
  #turn off button after data was loaded
  observeEvent(input$getdat,{
    rawDat(df)
    shinyjs::disable("getdat")
  })
  
  #do some subsequent data transformation of the raw data:
  Dat <- eventReactive(c(input$getdat,input$bin,input$reset), {
    req(rawDat())
    updateData(rawDat(),input$bin)
  })
  
  observeEvent(input$t0, {
    req(Dat(),timeformat())
    df<-updateStarttime(Dat(),input$t0)
    Dat(df)
  })
  
  output$plot <- renderPlotly({
    req(Dat())
    Dat() %>%  
      plot_ly() %>%
      add_trace(x= ~t, y = ~V1, type='scatter', mode='line', visible=TRUE) %>%
      layout(showlegend=FALSE)
  })
  
  observeEvent(input$reset,{
    rawDat(NULL)
    shinyjs::enable("getdat")
  })
  
}

shinyApp(ui,server)

Warning: Error in Dat: unused argument (df)

1

There are 1 answers

7
Stéphane Laurent On

You can't do Dat() <- something if Dat is a reactive conductor. Instead, use a reactive value and do Dat(something) to change its value to something.

server<- function(input, output, session) {
  
  Dat <- reactiveVal()
  
  #turn off button after data was loaded
  observeEvent(input$getdat,{
    Dat(df)
    shinyjs::disable("getdat")
  })
  
  output$plot <- renderPlotly({
    req(Dat())
    Dat() %>%  
      plot_ly() %>%
      add_trace(x= ~t, y = ~V1, type='scatter', mode='line', visible=TRUE) %>%
      layout(showlegend=FALSE)
  })
  
  observeEvent(input$reset,{
    Dat(NULL)
    shinyjs::enable("getdat")
  })
  
}

shinyApp(ui,server)