Created
March 5, 2017 19:20
-
-
Save bborgesr/e1ce7305f914f9ca762c69509dda632e to your computer and use it in GitHub Desktop.
How to use reactives in loops. What works, what doesn't and why.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# ------------------------------------------------------------------- | |
# ------------------ 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. | |
I also recommend reading this answer on Stack Overflow: https://stackoverflow.com/a/54331019/1445572
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Great overview! There is a typo that was copied to many of the comments: