--- title: "Bookmarking a Shiny app without Shiny bookmarking" author: "Stéphane Laurent" date: '2020-12-09' tags: R, shiny rbloggers: yes output: md_document: variant: markdown preserve_yaml: true html_document: highlight: kate keep_md: no highlighter: pandoc-solarized --- ```{r setup, include=FALSE} knitr::opts_chunk$set( echo = TRUE, collapse = TRUE, attr.source = ".numberLines", warning = FALSE, message = FALSE ) ``` I do not well remember, but it seems to me I faced some difficulties when I tried to use Shiny bookmarking to save and restore the state of a Shiny app. These difficulties arose when there were some `renderUI` in the app or an input updated with an `updateXXXinput` function. Then I'm using my own way for bookmarking the state of a Shiny app and restoring it. I'm saving the state of the app in a **rds** file and I restore the inputs delayed by a `renderUI` or an `updateXXXinput` function with the help of the `delay` function of the 'shinyjs' package. Below is an example ```{r eval=FALSE} library(shiny) library(shinyjs) ui <- fluidPage( useShinyjs(), br(), sidebarLayout( sidebarPanel( wellPanel( # restore the bookmarked state fileInput( "rds", "Restore state", accept = ".rds" ) ), wellPanel( # upload data from a CSV file fileInput( "csv", "Upload CSV", accept = ".csv" ) ), conditionalPanel( "output.uploaded", wellPanel( uiOutput("uiX"), # select the variable to be plotted checkboxInput( # whether to log-transform the variable "log10", "Log-transform", value = FALSE ) ), wellPanel( # bookmarking downloadButton( "saveState", "Save state" ) ) ) ), mainPanel( plotOutput("plot", width = "400px") ) ) ) server <- function(input, output, session){ data <- reactiveVal() # to store the uploaded data observeEvent(input[["csv"]], { # read and store the uploaded data csv <- input[["csv"]][["datapath"]] data(read.csv(csv)) }) output[["uploaded"]] <- reactive({ # indicator data uploaded !is.null(data()) }) outputOptions(output, "uploaded", suspendWhenHidden = FALSE) output[["uiX"]] <- renderUI({ # the widget for selecting a variable req(data()) selectInput( "X", "Select variable", choices = colnames(data()) ) }) Xloggable <- reactiveVal(FALSE) # indicates whether log-transform is possible observeEvent(input[["X"]], { loggable <- all(data()[[input[["X"]]]] > 0, na.rm = TRUE) Xloggable(loggable) }) logX <- reactive({ # indicates whether to log-transform the selected variable Xloggable() && input[["log10"]] }) observeEvent(list(input[["X"]], input[["log10"]]), { # prevents log-transform req(input[["X"]]) # if not possible if(input[["log10"]] && !Xloggable()){ showNotification("The selected variable cannot be log-transformed.") updateCheckboxInput(session, "log10", value = FALSE) } }) output[["plot"]] <- renderPlot({ # the plot req(input[["X"]]) x <- data()[[input[["X"]]]] if(logX()){ plot(log10(x), pch = 19) }else{ plot(x, pch = 19) } }) output[["saveState"]] <- downloadHandler( # bookmarking filename = "state.rds", content = function(file){ state <- list( data = data(), X = input[["X"]], log10 = input[["log10"]] ) saveRDS(state, file) } ) observeEvent(input[["rds"]], { # restore state # read the saved state state <- readRDS(input[["rds"]][["datapath"]]) # restore data data(state[["data"]]) delay(0, { delay(0, { # restore the selected variable updateSelectInput(session, "X", selected = state[["X"]]) delay(0, { # restore the checkbox state (log-transform) updateCheckboxInput(session, "log10", value = state[["log10"]]) }) }) }) }) } shinyApp(ui, server) ``` I firstly tried to delay the `updateSelectInput` directly in the `delay` function, like this: ```{r eval=FALSE} delay(0, { # restore the selected variable updateSelectInput(session, "X", selected = state[["X"]]) delay(0, { # restore the checkbox state (log-transform) updateCheckboxInput(session, "log10", value = state[["log10"]]) }) }) ``` Oddly, that worked in the RStudio browser, but not in Chrome. This is why I added a nested `delay` function. Bookmarking: ![](./figures/shinyBookmarking1.gif) And now, restoring: ![](./figures/shinyBookmarking2.gif)