gaspar
gaspar

Reputation: 1068

eventReactive recalculates already calculated objects with unchanged input

As I understand, eventReactive (or any reactive function) should not recalculate stuff whose related input did not change, but this is what's happening in my case. I'm pretty sure I'm doing something wrong but I just don't know what. In essence, I have two eventReactive functions, one involves a very time-consuming calculation, and the other mainly just plotting (should be quite quick). However, even when I change some inputs for plotting, the first eventReactive function is executed too (even though it's not needed).

Here is a shortened version of my code:

server <- function(input, output) {
    res_tabl <-
        eventReactive(c(input$recalc, input$recalc2), # this is a time-consuming calculation
                      ignoreNULL = FALSE, {
                          prep_sim(
                              gg_start = input$gg_start,
                              gg_end = input$gg_end
                          )
                      })
    threeplots <-
        eventReactive(c(input$recalc, input$recalc2), # this is for plotting
                      ignoreNULL = FALSE, {
                          prep_plot(
                              results_to_plot = res_tabl(),
                              yval_opt = input$yval_opt
                          )
                      })

    output$esdc_plot_comb <- renderPlot({
        threeplots()[[1]]
    })
    output$esdc_plot_tot <- renderPlotly({
        threeplots()[[2]]
    })
    output$esdc_plot_comb2 <- renderPlot({
        threeplots()[[1]]
    })
    output$esdc_plot_tot2 <- renderPlotly({
        threeplots()[[2]]
    })
    output$esdc_table <- renderDataTable({
        res_tabl()
    })
}

What should I do so that when I press a single Action button and I only changed input$yval_opt, only the second eventReactive content would run? (Nothing should run until I click the button.)

Less importantly – and perhaps this should be a separate question – as you can see I render each of the two returned plots twice. Is there perhaps a more efficient way to do this?

(The full code is available here.)

Upvotes: 1

Views: 290

Answers (1)

Waldi
Waldi

Reputation: 41210

This was tricky.

eventReactive(req(isTruthy(input$recalc1) | isTruthy(input$recalc2)), ignoreNULL = T,...
  • Added a reactiveVal() to keep track of last calculation update

I think following Minimal Reproducible example responds to your needs :

library(shiny)

# Define UI 
ui <- fluidPage(

    # Application title
    titlePanel("Test"),

    # Sidebar with a slider inpust 
    sidebarLayout(
        sidebarPanel(
            sliderInput("vizslider",
                        "viz percentage:",
                        min = 1,
                        max = 100,
                        value = 30),
            sliderInput("calcslider",
                        "Calculation duration (s):",
                        min = 1,
                        max = 10,
                        value = 2),
            actionButton("recalc1", "Calc 1"),
            actionButton("recalc2", "Calc 2"),
        ),

        # Show result
        mainPanel(
           textOutput("result")
        )
    )
)

# Define server logic 
server <- function(input, output) {
    lastcalc <- reactiveVal(0)
    run <- reactive({})
    calcresult <- eventReactive(req(isTruthy(input$recalc1) | isTruthy(input$recalc2)), ignoreNULL = T, {
        if (lastcalc()==input$calcslider)  {return("last calculation")} else {lastcalc(input$calcslider)}
        cat("Start calc for ",input$calcslider, "seconds\n")
        Sys.sleep(input$calcslider)
        cat("End calc \n")
        paste("calculation done in",input$calcslider,"seconds")

    })

    output$result <- eventReactive(c(input$recalc1,input$recalc2), ignoreNULL = T, {
       req(calcresult())
       paste("filter",input$vizslider,"% of a ",calcresult())
    })
}

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

Upvotes: 1

Related Questions