7
votes

Is there any possibility to update an input without reactives getting triggered?

Below I put a minimal example. The aim is to update the slider without the value in the main panel changing. When the slider is changed again, then it should be forwarded to dependent reactives again.

The question and the underlying use case is similiar to the following questions: R shiny - possible issue with update***Input and reactivity and Update SelectInput without trigger reactive?. Similiar to these questions, there is a reactive that depends on two Inputs in my use case. I want to update one of these input depending on the other, which results in the reactive getting calculated twice. However, both of these questions got around the problem by updating the input only selectively. This is not possible in my use case, since I want to have some information shown to the user by updating the input.

If there is no possibility to update an input without reactives getting triggered, I will ask a follow-up-question focusing on my use case.

Example

library(shiny)

ui <- fluidPage(
   titlePanel("Update Slider - Isolate reaction?"),

   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30),
         actionButton("set1", "set slider 'bins'$value=20"),
         actionButton("set2", "'ISOLATED' set slider 'bins'$value=20 ")
      ),

      mainPanel(
         textOutput("sliderValue")
      )
   )
)

# Define server logic 
server <- function(input, output, session) {
   output$sliderValue <- renderText(input$bins) 

   observeEvent(input$set1,{
     updateSliderInput(session,"bins",value=20)
   })
   observeEvent(input$set2,{
     ## Is there any possibility to update the slider without 'sliderValue' changing?
     #isolate does not work
     isolate(
       updateSliderInput(session,"bins",value=20 )
       )
   })
}

shinyApp(ui = ui, server = server)
2

2 Answers

4
votes

Here's a stab, though it feels like there might be side-effects from using stale data. Using the following diff:

 # Define server logic
 server <- function(input, output, session) {
-  output$sliderValue <- renderText(input$bins)
+  output$sliderValue <- renderText({ saved_bins(); })

+  update <- reactiveVal(TRUE)
+  saved_bins <- reactiveVal(30)
+
+  observeEvent(input$bins, {
+    if (update()) saved_bins(input$bins) else update(TRUE)
+  })
   observeEvent(input$set1,{
     updateSliderInput(session,"bins",value=20)
   })
   observeEvent(input$set2,{
     ## Is there any possibility to update the slider without 'sliderValue' changing?
     #isolate does not work
+    update(FALSE)
-    isolate(
       updateSliderInput(session,"bins",value=20 )
-    )
   })
 }

The method: using two new reactive values, one to store the data that (saved_bins) is used in the rendering, and one (update) to store whether that data should be updated. Everything that depends on input$bins should instead depend on saved_bins(). By using an additional observeEvent, the reactivity will always cascade as originally desired except when you explicitly set a one-time "do not cascade" with the prepended update(FALSE).

Full code below:

library(shiny)

ui <- fluidPage(
   titlePanel("Update Slider - Isolate reaction?"),

   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30),
         actionButton("set1", "set slider 'bins'$value=20"),
         actionButton("set2", "'ISOLATED' set slider 'bins'$value=20 ")
      ),

      mainPanel(
         textOutput("sliderValue")
      )
   )
)

# Define server logic 
server <- function(input, output, session) {
  output$sliderValue <- renderText({ saved_bins(); }) 

  update <- reactiveVal(TRUE)
  saved_bins <- reactiveVal(30)

  observeEvent(input$bins, {
    if (update()) saved_bins(input$bins) else update(TRUE)
  })
  observeEvent(input$set1,{
    updateSliderInput(session,"bins",value=20)
  })
  observeEvent(input$set2,{
    ## Is there any possibility to update the slider without 'sliderValue' changing?
    #isolate does not work
    update(FALSE)
    updateSliderInput(session,"bins",value=20)
 })
}

shinyApp(ui = ui, server = server)
0
votes

Firstly credit to @r2evans's solution.

At the risk of a verbal thrashing from the many headteacheRs that prohibit it, to avoid double observer you can use global assignment. Sensible to use a less generic name than 'update' though.

library(shiny)

ui <- fluidPage(
  titlePanel("Update Slider - Isolate reaction?"),
  
  sidebarLayout(
    sidebarPanel(
      sliderInput("bins",
                  "Number of bins:",
                  min = 1,
                  max = 50,
                  value = 30),
      actionButton("set1", "set slider 'bins'$value=20"),
      actionButton("set2", "'ISOLATED' set slider 'bins'$value=20 ")
    ),
    
    mainPanel(
      textOutput("sliderValue")
    )
  )
)

# Define server logic 
server <- function(input, output, session) {
  output$sliderValue <- renderText({ saved_bins(); }) 
  
  saved_bins <- reactiveVal(30)
  
  observeEvent(input$bins, {
    if (update) saved_bins(input$bins) else update <<- TRUE
  })
  observeEvent(input$set1,{
    updateSliderInput(session,"bins",value=20)
  })
  observeEvent(input$set2,{
    ## Is there any possibility to update the slider without 'sliderValue' changing?
    #isolate does not work
    update <<- FALSE
    updateSliderInput(session,"bins",value=20)
  })
}

shinyApp(ui = ui, server = server)