user1329307
user1329307

Reputation: 161

More than two dependent selectizeInputs cause an infinite loop in R Shiny

In the simple example below, I cannot select state 3 after selecting states 1 and 2. It works okay with 2 states, but as soon as I go to three states it infinite loops through the ObserveEvents. If it were the triggers from updating, I'd expect it to infinitely loop after selecting just the first state, but it doesn't.

I came across the freezeReactiveValue() function but when I freeze the reactiveValues in an attempt to not run the observeEvent, the selectizes don't update to prevent the user from selecting the same state twice.

Any ideas?

STATES <- c("AL", "AK", "AZ", "AR", "CA", "CO", "CT",
            "DE", "DC", "FL", "GA", "HI", "ID", "IL",
            "IN", "IA", "KS", "KY", "LA", "ME", "MD",
            "MO", "MT", "NE", "NV", "NH", "NJ", "NM",
            "NY","NC", "ND", "OH", "OK", "OR", "PA",
            "PR", "RI", "SC", "SD", "TN", "TX", "UT",
            "VT", "VA", "VI", "WA", "WV", "WI", "WY")
ui <- fluidPage(fluidRow(
  selectizeInput("state1", label = "State", 
                 choices = STATES,
                 selected = NULL,
                 multiple = FALSE,
                 options = list(placeholder = "Select a State",
                                onInitialize = I('function() { this.setValue(""); }'))),
  
  selectizeInput("state2", label = "State", 
                 choices = STATES,
                 selected = NULL,
                 multiple = FALSE,
                 options = list(placeholder = "Select a State",
                                onInitialize = I('function() { this.setValue(""); }'))),
  
  selectizeInput("state3", label = "State", 
                 choices = STATES,
                 selected = NULL,
                 multiple = FALSE,
                 options = list(placeholder = "Select a State",
                                onInitialize = I('function() { this.setValue(""); }')))
))

server <- function(input, output, session) ({
  observeEvent(input$state1, {
    updateSelectizeInput(
      session, "state2",
      choices = setdiff(STATES,
                        c(input$state1, input$state3)),
      selected = input$state2
    )
    updateSelectizeInput(
      session, "state3",
      choices = setdiff(STATES,
                        c(input$state1, input$state2)),
      selected = input$state3
    )
  })
  
  observeEvent(input$state2, {
    updateSelectizeInput(
      session, "state1",
      choices = setdiff(STATES,
                        c(input$state2, input$state3)),
      selected = input$state1
    )
    updateSelectizeInput(
      session, "state3",
      choices = setdiff(STATES,
                        c(input$state1, input$state2)),
      selected = input$state3
    )
  })
  
  observeEvent(input$state3, {
    updateSelectizeInput(
      session, "state1",
      choices = setdiff(STATES,
                        c(input$state2, input$state3)),
      selected = input$state1
    )
    updateSelectizeInput(
      session, "state2",
      choices = setdiff(STATES,
                        c(input$state1, input$state3)),
      selected = input$state3
    )
  })
  
  
})

shinyApp(ui = ui, server = server)

Upvotes: 1

Views: 184

Answers (1)

Jan
Jan

Reputation: 9108

As you already noticed, the onInitialize = I('function() { this.setValue(""); }') causes the infinite loop since it gets called every time when updateSelectizeInput runs.

As an alternative you could do the following: Since the value of the selectizeInput shall be set to "" when starting the app, you can also set choices = NULL inside your ui. This will automatically set the initial value to "". In order for then making the elements of STATES selectable, put them into an reactiveVal inside your server

states <- reactiveVal(STATES)

and set the choices parameter of the updateSelectizeInput such that it uses states():

choices = setdiff(states(), c(input$state1, input$state3))

Notice that for the below MWE I also corrected a presumed typo in your last updateSelectizeInput (set selected = input$state2 instead of input$state3).

enter image description here

STATES <- c("AL", "AK", "AZ", "AR", "CA", "CO", "CT",
            "DE", "DC", "FL", "GA", "HI", "ID", "IL",
            "IN", "IA", "KS", "KY", "LA", "ME", "MD",
            "MO", "MT", "NE", "NV", "NH", "NJ", "NM",
            "NY","NC", "ND", "OH", "OK", "OR", "PA",
            "PR", "RI", "SC", "SD", "TN", "TX", "UT",
            "VT", "VA", "VI", "WA", "WV", "WI", "WY")

ui <- fluidPage(fluidRow(
    selectizeInput(
        "state1",
        label = "State",
        choices = NULL,
        selected = NULL,
        multiple = FALSE,
        options = list(placeholder = "Select a State")
    ),
    
    selectizeInput(
        "state2",
        label = "State",
        choices = NULL,
        selected = NULL,
        multiple = FALSE,
        options = list(placeholder = "Select a State")
    ),
    
    selectizeInput(
        "state3",
        label = "State",
        choices = NULL,
        selected = NULL,
        multiple = FALSE,
        options = list(placeholder = "Select a State")
    )
))

server <- function(input, output, session)
    ({
        states <- reactiveVal(STATES)
        
        observeEvent(input$state1, {
            updateSelectizeInput(
                session,
                "state2",
                choices = setdiff(states(),
                                  c(input$state1, input$state3)),
                selected = input$state2
            )
            updateSelectizeInput(
                session,
                "state3",
                choices = setdiff(states(),
                                  c(input$state1, input$state2)),
                selected = input$state3
            )
        })
        
        observeEvent(input$state2, {
            updateSelectizeInput(
                session,
                "state1",
                choices = setdiff(states(),
                                  c(input$state2, input$state3)),
                selected = input$state1
            )
            updateSelectizeInput(
                session,
                "state3",
                choices = setdiff(states(),
                                  c(input$state1, input$state2)),
                selected = input$state3
            )
        })
        
        observeEvent(input$state3, {
            updateSelectizeInput(
                session,
                "state1",
                choices = setdiff(states(),
                                  c(input$state2, input$state3)),
                selected = input$state1
            )
            updateSelectizeInput(
                session,
                "state2",
                choices = setdiff(states(),
                                  c(input$state1, input$state3)),
                selected = input$state2
            )
        })
        
    })

shinyApp(ui = ui, server = server)

Upvotes: 1

Related Questions