Disconnected, Reconnect and Reload

2024-10-04

Custom disconnected screen

Nobody likes it when an app stops responding. Traditionally, Shiny apps will be “grayed out”. Since this doesn’t provide a nice user experience, {shinyMobile} adds a custom disconnected screen. This screen (a toast) is displayed when the app is disconnected from the server.

library(shiny)
library(shinyMobile)

shinyApp(
  ui = f7Page(
    options = list(dark = FALSE),
    title = "My app",
    f7SingleLayout(
      navbar = f7Navbar(
        title = "Custom disconnected screen",
        hairline = FALSE
      ),
      f7List(
        inset = TRUE,
        dividers = FALSE,
        strong = TRUE,
        f7Text(
          inputId = "text",
          label = "Text input",
          value = "Some text",
          placeholder = "Your text here"
        ),
        f7Select(
            inputId = "select",
            label = "Make a choice",
            choices = 1:3,
            selected = 1
          )
      ),
      f7Block(
        f7Button(inputId = "disconnect",
                 label = "Disconnect me")
      ),
      f7Block(
        verbatimTextOutput("mytext"),
        verbatimTextOutput("myselect")
      )
    )
  ),
  server = function(input, output) {
    # set ignoreInit to avoid the observer being triggered after reconnect
    # this happens because of restoring the app's state
    observeEvent(input$disconnect, ignoreInit = TRUE, {
      print(input$disconnect)
      stop("Oops, I disconnected you!")
    })
    
    output$mytext <- renderText({
      input$text
    })
    
    output$myselect <- renderText({
      input$select
    })
  }
)

The user is given two options: either to reconnect or to reload the app. There’s a subtle difference between the two, which is explained below.

Reconnect

This button will attempt to reconnect to the server. If the server is not available, or encounters an error again, the custom disconnected screen will be displayed again.


When reconnecting, Shiny attempts to restore the app to its previous state. This means that the app will be in the same state as before the disconnection. In the above code, this means input$disconnect, input$text and input$select will be restored to their previous values. The user actually doesn’t see that they are “restored”: it looks like the user never left. But it is important to realize that behind the scenes, Shiny is starting from the initial state of your app and sets back the values of the inputs to their previous values. This means observers and reactive expressions will be triggered again. In the code above, we’re using ignoreInit to avoid the observer being triggered after reconnect. If we would omit this, we would end up being disconnected again. After all, the value of input$disconnect changes from 0 to 1 when clicked.


Note that the JS code behind the reconnect button ignores the user reconnect setup and proposes reconnecting regardless of the session$allowReconnect configuration. A solution is provided in the Outstanding User Interfaces with Shiny book.

Reload

The custom disconnected screen also includes a reload button. This button will reload the app (like hitting refresh in your browser). This is useful when the app is stuck in a loop and the reconnect button doesn’t work. Compared to reconnecting, reloading will start from a clean slate: there’s no restoring of values.


Note that when setting your app up to be a PWA, there’s an offline.html asset generated when using charpente::set_pwa(). This page will be displayed when the user is offline. This page contains a reload button that will reload the app. It is not reconnecting.