renderUI uiOutput acts differently based on how list of outputs is defined

In R, the for loop iterator is a single variable shared by each loop iteration. When a loop ends, the iterator variable sticks around and contains the last element in the sequence. This often leads to unexpected behavior when creating functions (or closures) in the loop that access the iterator. If these functions are called after the loop ends, they'll always use the final value of the iterator, not the value at the time they were created.

An example -

funcs <- list()

for (i in 1:3)
  funcs[[i]] <- function() print(i)

print(i)
## [1] 3

for (f in funcs)
  f()
## [1] 3
## [1] 3
## [1] 3

That's essentially what's happening here since the Shiny renderXX functions create render functions that aren't called until their associated outputs are ready to show. By the time any of these render functions get called, the loop has already ended and i will be at length(list_of_tables).

        for (i in 1:length(list_of_tables)) {
          myCollapse[[i]] <- bsCollapsePanel(i, renderPrint(list_of_tables[[i]]))
        }

You can get around this by creating a new scope for each loop iteration using local() or a function. I made some examples of this a while ago: Shiny app with dynamic number of datatables

But I recommend using the apply functions (like @paul showed) over for-loops whenever possible. I think it's the easiest and most predictable.

2 Likes