Search code examples
rshinyr-plotly

R shiny with plotly: invalidation still occurring inside isolate and observeEvent


Consider a simple plotly scatterplot that is dependent on a user selection (via selectInput) that selects the variable to plot on a particular axis. We want to update the plot with the click of a button, rather than when the user interacts with the selectInput. So, we wrap the plot-update logic in an observeEvent that takes a dependency on the button. The observeEvent updates a reactiveValues object that contains the plot. (This example uses reactiveValues because we're demonstrating this with both ggplot and plotly plots.)

When the app is run, before clicking the actionButton, changing the selected variable from selectInput does not update the plot (as expected). However, after the actionButton has been clicked once, any further change to the selectInput invalidates the plotly plot instantly and causes it to re-render. This behaviour does not occur with the ggplot plot (which only updates when the actionButton is clicked).

I've also tried wrapping the reactive value that defines the plotly plot in isolate, and create an explicit dependency on the input$update_plts button, but that makes no difference.

library(shiny)
library(ggplot2)
library(plotly)

ui <- {
    fluidPage(
        selectInput('select_xvar', label = 'select x var', choices = names(mtcars)),
        actionButton('update_plts', 'Update plots'),
        plotOutput('plt1'),
        plotlyOutput('plt2')
    )
}

server <- function(input, output, session) {
    
    val <- reactiveValues(
        plt1 = ggplot(mtcars) +
            geom_point(aes(x = wt, y = mpg)
        ),
        plt2 = plot_ly(mtcars,
            x = ~wt,
            y = ~mpg
        )
    )
    
    observeEvent(input$update_plts, {
        val$plt1 = ggplot(mtcars) +
            geom_point(aes_string(
                x = input$select_xvar,
                y = 'mpg'
            ))
        val$plt2 = plot_ly(mtcars,
            x = ~get(input$select_xvar),
            y = ~mpg)
    })
    
    output$plt1 <- renderPlot(val$plt1)

    # initial attempt with no isolate
    # output$pl2 <- renderPlotly(val$plt2)

    # second attempt with isolate, still invalidates instantaneously
    output$plt2 <- renderPlotly({
        input$update_plts
        isolate(val$plt2)
    })
}

shinyApp(ui, server)

Any ideas why this is the case with plotly? The only reference to this I could find is a closed Github issue that is apparently unresolved.

  • R version 3.6.0
  • plotly version 4.9.2.1
  • shiny version 1.5.0

Solution

  • Not sure why, but it works as expected when you isolate inputs in the plot_ly call:

    val$plt2 = plot_ly(mtcars,
                       x = ~get(isolate(input$select_xvar)),
                       y = ~mpg)
    

    You can then remove isolate and actionButton from renderPlotly:

    output$plt2 <- renderPlotly(val$plt2)