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)
You can't do
Dat() <- somethingifDatis a reactive conductor. Instead, use a reactive value and doDat(something)to change its value tosomething.