Reactive dependencies broken - strange behaviour

23 views
Skip to first unread message

Bartek Chroł

unread,
Dec 2, 2016, 5:56:09 AM12/2/16
to Shiny - Web Framework for R
I have quite large app with modules and a lot of dynamic UI, and I've encountered a strange situation when one reactive expression seems to not re-execute when its dependecy has changed. The following example is a bit complicated, but it's the shortest for which error occurs. So briefly - app contains nothinf but single module. Inside module we have first select input (`in_no_options`) for choosing the number of options for the next select input (`in_option`). The list of options is created in reactive expression `available_options`. The second select is created as dynamic UI because it depends on output from `available_options`. `selected_option` is a reactive expression returning selected option. There is also an observer printing selected option to console.
Now, in the following app reactive `selected_option` doesn't work. I've looked on reactLog and it looks like `selected_option` isn't even associated with `in_option`.

library("shiny")

module_UI <- function(id) {
  ns <- NS(id)
  
  tagList(
    selectInput(ns("in_no_options"), label = "Number of options:", choices = 10:20, selected = 10),
    uiOutput(ns("ui_in_option"))
  )
}



module <- function(input, output, session) {
  
  ns <- session$ns
  
  available_options <- reactive({
    tmp <- seq_len(req(input$in_no_options))
    res <- letters[tmp]
    names(res) <- letters[tmp]
    res
  })
  
  
  output$ui_in_option <- renderUI({
    selectInput(ns("in_option"), label = "Options:",
                choices = available_options())
  })
  
  
  selected_option <- reactive({
    opt <- isolate(available_options())
    selected <- req(input$in_option)
    opt[[selected]]
  })
  
  observeEvent(selected_option(), {print(sprintf("New option: %s", selected_option()))})
  
}



ui <- basicPage(htmlOutput("ui"))


server <- function(input, output, session) {
  
  output$ui <- renderUI(module_UI("module"))
  
  callModule(module, "module")
  
}

runApp(shinyApp(ui, server))


I've tried a few things and here is the list of things that "fixes" reactive behaviour (starting from the most strange):
- putting `selected <- req(input$in_option)` line BEFORE `opt <- isolate(available_options())` line in `selected_option`
- skipping `output$ui` and calling `module_UI` directly inside `basicPage`
- adding some output that depends on `selected_option`, textOutput for example 
- running the same code directly inside `server` and not using module

I've no idea why this doesn't work in the first place.


Joe Cheng

unread,
Dec 2, 2016, 10:11:09 AM12/2/16
to Bartek Chroł, Shiny - Web Framework for R
If you have this:

a <- reactive({
req(input$x)
input$y
})

and input$x is falsy, then execution aborts at the input$x line (a special error object is thrown). No relationship will be established with input$y--until input$x becomes truthy. This is correct because while x is falsy there's no possible way y could ever effect the outcome.

Now if you do this:

a <- reactive({
isolate(req(input$x))
input$y
})

Urk. Since we've isolated the reading of input$x, changing its value to something truthy will not cause the reactive expression to invalidate.

Why are you isolating the read to available_options? Maybe remove that isolate and if the concern is that the observer runs too often, change it to observeEvent(input$in_option, ...)
--
You received this message because you are subscribed to the Google Groups "Shiny - Web Framework for R" group.
To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/shiny-discuss/4d58f1fb-d50f-462f-8cc6-63753c3dc41b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Bartek Chroł

unread,
Dec 2, 2016, 10:39:39 AM12/2/16
to Shiny - Web Framework for R, bartek...@gmail.com
Well, in this example isolating available_options isn't very reasonable, but in my app a lot of different things happen in between and it seems right way to do it. I guess I was wrong :) Defing selected_option as eventReactive(input$in_option, {...}) is a proper solution.

Let me translate it to my example: because input$in_no_options is inside renderUI, it is NULL - meaning falsy - in the beginning, and therefore available_options is also falsy and because of that selected_option is also falsy. Now when available_options are calculated - and not falsy - it doesn't change anything, because they were isolated (even if input$in_option is not falsy now). If input$in_no_options isn't in renderUI but directly in ui function than expressions are executed in different order and the above trap is avoided. Am I right?

Joe Cheng

unread,
Dec 2, 2016, 12:31:18 PM12/2/16
to Bartek Chroł, Shiny - Web Framework for R
eventReactive is a great solution here, sure.

Here's an interesting way to tackle the general problem of "I only care if it's falsy" or whatever. I'll use Hadley's shinySignals package: https://github.com/hadley/shinySignals

available_options <- reactive(...) # as you have it currently
has_available_options <- shinySignals::dropRepeats(reactive(
  isTruthy(available_options)
))
selected_option <- reactive({
  has_options <- has_available_options()
  ...
})

In your case I'd just use eventReactive but I thought this was an interesting way to filter down the event stream of available_options to only that which you care about.

Bartek Chroł

unread,
Dec 2, 2016, 2:43:03 PM12/2/16
to Joe Cheng, Shiny - Web Framework for R
I didn't know about shinySignals, I'll surely look into it. Thanks!

To unsubscribe from this group and stop receiving emails from it, send an email to shiny-discuss+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages