Axel Facius
Axel Facius

Reputation: 78

renderPlot issue when rendering a list of plots

I'm writing an R shiny app which should allow the user to create customisable plots of some data. The idea is that my app offers a "create new plot" button, which renders the plot and stores it in a reactive. A renderUI function "watches" this list and renders all plots in that reactive.

I found a couple of related questions r-markdown-shiny-renderplot-list-of-plots-from-lapply or shiny-r-renderplots-on-the-fly which however did not really help in my case. I hope I didn't miss a good answer somewhere (which I would assume there is because I think this is not a rare use case).

When implementing, I noticed a strange behaviour: When there is only one plot to be shown, everything works well. However, when I have n (n>1) plots, instead of rendering plot 1, plot 2, ..., plot n, the app only showed n times the plot n.

See my example app below. I simplified the problem by just letting the user choose the number of plots to be displayed. The renderUI function then has a loop creating thees plots in a variable p and then calls renderPlot(p). I assume shiny does some caching and for some reason fails to recognise that p changes in the loop?!

I found a workaround by replacing the renderPlot(p) by do.call("renderPlot", list(expr = p). This does the job but I'm still curious to learn why the direct renderPlot does not work.

Here is my example app:

library(shiny)
library(ggplot2)


# Define UI
ui <- shinyUI(fluidPage(
  titlePanel("renderPlot Test"),

  sidebarLayout(
    sidebarPanel(
      numericInput(inputId = "n", label = "Number of Plots", value = 1L, min = 1L, max = 5L, step = 1L),
      checkboxInput(inputId = "use_do.call", label = "use 'do.call'", value = FALSE)
    ),

    mainPanel(
      uiOutput("show_plots")
    )
  )
))

# Define server logic
server <- shinyServer(function(input, output) {

  output$show_plots <- renderUI({
    ui <- tags$div(tags$h4("Plots"))

    for( i in 1:input$n ) {
      p <- ggplot() + ggtitle(paste("plot", i))
      if( input$use_do.call ) { # this works
        ui <- tagAppendChild(ui, do.call("renderPlot", args=list(expr=p, width = 200, height = 200)))
      } else {                  # this doesn't ...
        ui <- tagAppendChild(ui, renderPlot(p, width = 200, height = 200))
      }
    }
    return(ui)
  })
})

# Run the application
shinyApp(ui = ui, server = server)

Upvotes: 0

Views: 1236

Answers (1)

Glen Smith
Glen Smith

Reputation: 146

I agree with @JonMinton, and I've had the same problem. I've found that when I reuse the same variable to save the plots and render them (such as what you do with p), the plots get overwritten by the next plot and only the final plot is copied n times like you said.

To get around this, I define a new variable for each plot, which may not be sustainable for your project, but it is a workaround.

Upvotes: 0

Related Questions