2
votes

I am struggling to understand how isolate() and reactive() should be used in R Shiny.

I want to achieve the following:

Whenever the "Refresh" action button is clicked:

  • Perform a subset on a data.frame and,

  • Feed this into my function to recalculate values.

The subset depends on a group of checkboxes that the user has ticked, of which there are approximately 40. I cannot have these checkboxes "fully reactive" because the function takes about 1.5 sec to execute. Instead, I want to give the user a chance to select multiple boxes and only afterwards click a button to (a) subset and (b) call the function again.

To do so, I load the data.frame in the server.R function:

df1 <- readRDS("D:/././df1.RData")

Then I have my main shinyServer function:

shinyServer(function(input, output) {

  data_output <- reactive({
    df1 <- df1[,df1$Students %in% input$students_selected] 

    #Here I want to isolate the "students_selected" so that this is only 
    #executed once the button is clicked
  })

  output$SAT <- renderTable({
    myFunction(df1)
  })
}
2

2 Answers

3
votes

How about something like

data_output <- eventReactive(input$button, {
    df1[,df1$Students %in% input$students_selected] 
})

Here is my minimal example.

library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5), 
           actionButton("btn", "update"),
           tableOutput("tbl")) 
server <- function(input, output) {
  data_output <- eventReactive(input$btn, {
    data.frame(id = 1:10, x = 11:20)[seq(input$num), ]
  })

  output$tbl <- renderTable({
    data_output()})
}

runApp(list(ui = ui, server = server))

Edit

Another implementation, a bit more concise. renderTable by default inspects the changes in all reactive elements within the function (in this case, input$num and input$button). But, you want it to react only to the button. Hence you need to put the elements to be ignored within the isolate function. If you omit the isolate function, then the table is updated as soon as the slider is moved.

library(shiny)
ui <- list(sliderInput("num", "rowUpto", min= 1, max = 10, value = 5), 
           actionButton("btn", "update"),
           tableOutput("tbl")) 
server <- function(input, output) {
  output$tbl <- renderTable({
    input$btn
    data.frame(id = 1:10, x = 11:20)[seq(isolate(input$num)), ]
  })
}

runApp(list(ui = ui, server = server))
2
votes

Use eventReactive instead:

data_output <- eventReactive(input$updateButton, {

    df1 <- df1[,df1$Students %in% input$students_selected] #I think your comments are messed up here, but I'll leave the filtering formatting to you

  })

  output$SAT <- renderTable({
    data_output()
  })

And in your UI you should have something like:

actionButton('updateButton',label = "Filter")

Looking at ?shiny::eventReactive:

Use eventReactive to create a calculated value that only updates in response to an event. This is just like a normal reactive expression except it ignores all the usual invalidations that come from its reactive dependencies; it only invalidates in response to the given event.