Appsilon / shiny.router

A minimalistic router for your Shiny apps.
http://appsilon.github.io/shiny.router
Other
255 stars 31 forks source link

Tables double loading when using browser back button #69

Closed jwmortensen closed 4 years ago

jwmortensen commented 4 years ago

Issue:

Currently, when I click a link in the app and then go back to the previous page, the tables on it appear to be cached from when I was on that page previously. After a moment, the table will flash and reload. Initially I thought this might be due to some reactive dependencies, but even when I removed all dependencies and just loaded the table by itself, the behavior persisted. I would like to devise a solution that either clears that cached version of the table so that there is no double loading, or prevent the reloading if the cached table hasn't changed. Is there a known solution to this problem?

Environment:

Shiny: 1.4.0.2 shiny.router: 0.1.1 R: 3.6.1

How to reproduce issue:

I've included code to reproduce this problem. To see the double load issue, run the app and wait for the table to load. Then click the link to the second page, and click the back button in the browser. You should see the table from before, then after 1 second, there should be a flash as the table is reloaded. I tested this on both Chrome and Firefox and was able to get the same results in both browsers.

library(shiny)
library(shiny.router)

# This generates menu in user interface with links.
menu <- (
  tags$ul(
    tags$li(a(class = "item", href = route_link("/"), "First page")),
    tags$li(a(class = "item", href = route_link("second"), "Second page"))
  )
)

# This creates UI for each page.
page <- function(title, content = "") {
  div(
    menu,
    titlePanel(title),
    p(content)
  )
}

# Both sample pages.
root_page <- page("Home page", tagList(DT::dataTableOutput("table")))
other_page <- page("Second page")

# callbacks
root_callback <- function(input, output, session) {
  output$table = DT::renderDataTable({
    Sys.sleep(1)
    DT::datatable(EuStockMarkets)
  })
}

# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
  route("/", root_page, root_callback),
  route("second", other_page)
)

# Creat output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
  router_ui()
))

# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
  router(input, output, session)
})

# Run server in a standard way.
shinyApp(ui, server)
krystian8207 commented 4 years ago

Hi @jwmortensen Thank you for noticing this issue.

In fact the table is not loaded twice. After you return to the first page, the old version of table is displayed and then, after one second, the table is rerendered. Please see the below example:

library(shiny)
library(shiny.router)

# This generates menu in user interface with links.
menu <- (
  tags$ul(
    tags$li(a(class = "item", href = route_link("/"), "First page")),
    tags$li(a(class = "item", href = route_link("second"), "Second page"))
  )
)

# This creates UI for each page.
page <- function(title, content = "") {
  div(
    menu,
    titlePanel(title),
    p(content)
  )
}

# Both sample pages.
root_page <- page("Home page", tagList(DT::dataTableOutput("table")))
other_page <- page("Second page")

# callbacks
root_callback <- function(input, output, session) {
  output$table = DT::renderDataTable({
    Sys.sleep(1)
    DT::datatable(EuStockMarkets[sample(1:10, 10), ])
  })
}

# Creates router. We provide routing path, a UI as
# well as a server-side callback for each page.
router <- make_router(
  route("/", root_page, root_callback),
  route("second", other_page)
)

# Creat output for our router in main UI of Shiny app.
ui <- shinyUI(fluidPage(
  router_ui()
))

# Plug router into Shiny server.
server <- shinyServer(function(input, output, session) {
  router(input, output, session)
})

# Run server in a standard way.
shinyApp(ui, server)

The previous table is displayed because it is still remembered in shiny reactive graph before the new table is rendered.

You can remove cached table object by adding the below observer:

server <- shinyServer(function(input, output, session) {
  router(input, output, session)
  observeEvent(shiny.router::get_page(), {
    if (shiny.router::get_page() != "/") {
      output$table <- NULL
    }
  })
})

Please let me know what behavior do you expect in this example. I'll do my best to help you with reaching the expected result.