Skip to content

Instantly share code, notes, and snippets.

@dsvanidze
Forked from bborgesr/reactives-in-loops.R
Created March 23, 2024 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsvanidze/5f2e68d19253fd14a2968e39e93f8b0c to your computer and use it in GitHub Desktop.
Save dsvanidze/5f2e68d19253fd14a2968e39e93f8b0c to your computer and use it in GitHub Desktop.
How to use reactives in loops. What works, what doesn't and why.
# -------------------------------------------------------------------
# ------------------ REACTIVES INSIDE FOR LOOPS ---------------------
# -------------------------------------------------------------------
# -------------------------------------------------------------------
# --- EXAMPLE 1: this works fine, because there are no reactives in -
# --- the for lopp --------------------------------------------------
# -------------------------------------------------------------------
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt")
)
server <- function(input, output, session) {
rv <- reactiveValues(one = 1, two = 2, three = 3)
output$txt <- renderPrint({
vals <- list()
for (val in reactiveValuesToList(rv)) {
vals[[val]] <- val
}
for (i in seq_len(length(vals))) {
print(vals[[i]])
}
})
}
shinyApp(ui, server)
#> [1] 1
#> [1] 2
#> [1] 3
# -------------------------------------------------------------------
# --- EXAMPLE 2: this works fine, because even though there is a ----
# --- reactive in the for lopp, it is invoked immediately (inside ---
# --- the for loop) -------------------------------------------------
# -------------------------------------------------------------------
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt")
)
server <- function(input, output, session) {
rv <- reactiveValues(one = 1, two = 2, three = 3)
output$txt <- renderPrint({
vals <- list()
for (val in reactiveValuesToList(rv)) {
vals[[val]] <- reactive({ val })()
}
for (i in seq_len(length(vals))) {
print(vals[[i]])
}
})
}
shinyApp(ui, server)
#> [1] 1
#> [1] 2
#> [1] 3
# -------------------------------------------------------------------
# --- EXAMPLE 3: this does not work, because there is a reactive in -
# --- the for lopp that is only invoked outside the for loop --------
# -------------------------------------------------------------------
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt")
)
server <- function(input, output, session) {
rv <- reactiveValues(one = 1, two = 2, three = 3)
output$txt <- renderPrint({
vals <- list()
for (val in reactiveValuesToList(rv)) {
vals[[val]] <- reactive({ val })
}
for (i in seq_len(length(vals))) {
print(vals[[i]]())
}
})
}
shinyApp(ui, server)
#> [1] 3
#> [1] 3
#> [1] 3
# --- Joe on this behavior:
# --- > It's because all the iterations of the for loop share the same
# --- > reference to el. So when any of the created reactive expressions
# --- > execute, they're using whatever the final value of el was.
# -------------------------------------------------------------------
# --- EXAMPLE 4: this works, because even though there is a reactive
# --- in the for lopp that is only invoked outside of it, it is: ----
# --- ----
# --- * wrapped in a call to `local({ })`; ----
# --- ----
# --- * the iter variable (`val`) is assigned to `local({ })` ----
# --- specific variable (`myVal`); ----
# --- ----
# --- * the super-assign operator (`<-`) is used ----
# -------------------------------------------------------------------
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt")
)
server <- function(input, output, session) {
rv <- reactiveValues(one = 1, two = 2, three = 3)
output$txt <- renderPrint({
vals <- list()
for (val in reactiveValuesToList(rv)) {
local({
myVal <- val
vals[[myVal]] <<- reactive({ myVal })
})
}
for (i in seq_len(length(vals))) {
print(vals[[i]]())
}
})
}
shinyApp(ui, server)
#> [1] 1
#> [1] 2
#> [1] 3
# --- Joe on this behavior:
# --- > You can fix this by using a for loop but introducing a
# --- > local({...}) inside of there, and creating a local variable
# --- > in there whose value is assigned to el outside of the reactive.
# -------------------------------------------------------------------
# --- EXAMPLE 5: this works, because we use lapply instead of a for -
# --- loop ----------------------------------------------------------
# -------------------------------------------------------------------
library(shiny)
ui <- fluidPage(
verbatimTextOutput("txt")
)
server <- function(input, output, session) {
rv <- reactiveValues(one = 1, two = 2, three = 3)
output$txt <- renderPrint({
vals <- lapply(
reactiveValuesToList(rv),
function(x) { reactive(x) }
)
# --- Note that this alternative would not work because if doesn't
# --- get its own reference to the iter variable, so we always ---
# --- need to create a wrapper for the reactive ------------------
# vals <- lapply(
# reactiveValuesToList(rv),
# reactive
# )
for (i in seq_len(length(vals))) {
print(vals[[i]]())
}
})
}
shinyApp(ui, server)
#> [1] 1
#> [1] 2
#> [1] 3
# --- Joe on this behavior:
# --- > You can fix this by using lapply instead of a for loop; since
# --- > each iteration executes as its own function call, it gets its
# --- > own reference to el.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment