In shiny apps, it can be difficult to use iterative self-referring.
This caused me a great deal of head scratching, but ...
In shiny apps, it can be difficult to use iterative self-referring.
This caused me a great deal of head scratching, but ...
Im posting this to help if anyone else has problems trying to carry a value over from one 'iteration' to the next in a shiny app.
In my case, I need to calculate a running mean.
library(shiny)
# UI
{
ui <- fluidPage(
textOutput('serial')
)
}
server <- function(input, output, session) {
serialStream <- reactive({
invalidateLater(10, session)
return(sample(1:100,1))
})
# Non-solution #1
# runningMean <- reactive({
# isolate(runningMean()*0.4) + serialStream()*0.1
# })
# output$serial <- renderText(runningMean())
# Non-solution #2
# runningMean <- 0 # initial value
# observe({
# runningMean <<- runningMean*0.4 + serialStream()*0.1
# })
# output$serial <- renderText(runningMean)
# Solution
rm <- 0 # initial value
runningMean <- reactive({
rm <<- rm * 0.4 + serialStream()*0.1
rm
})
output$serial <- renderText(runningMean())
}
# Run the application
shinyApp(ui = ui, server = server)
Something similar can be accomplished using observe(), but I ran into problems with memory leakage. The code above does the trick for me... Note the use of <<- instead of <- inside the reactive function. This ensures the value of rm is preserved outside that function.
Your proposed solution is not the correct thing to do in shiny, I strongly recommend against doing that. Do not use <<-
for assignment, it is almost never the correct thing to do in shiny, it may "seem" right but has unintended behaviour that can lead to weird bugs. Usually when you want to use <<-
, the correct thing to do is to use reactiveVal()
.
Consider the app below that uses <<-
to save running sum of a number (I'm using a different sample app than yours to make it simpler and easier to understand):
ui <- fluidPage(
actionButton("add1", "Add 1"),
actionButton("add5", "Add 5")
)
server <- function(input, output) {
mysum <<- 0
observeEvent(input$add1, {
mysum <<- mysum + 1
print(mysum)
})
observeEvent(input$add5, {
mysum <<- mysum + 5
print(mysum)
})
}
shinyApp(ui, server)
The above app will seem to work fine when you test it, but when you put it out in the real world you would notice a bug: if you open two browser tabs, the variable is shared between the two. In fact, the number will be shared across all users. This won't happen in your app because your initial assignment used <-
while the second one uses <<-
, but the above pattern is something I see very often so I wanted to explicitly show it. The other thing that is wrong here is that now you've left the reactivity world - mysum
is not a reactive variable, so we can't use it in any reactive context in shiny (this is why your non-solution #2 didn't work). Below is the better approach:
ui <- fluidPage(
actionButton("add1", "Add 1"),
actionButton("add5", "Add 5")
)
server <- function(input, output) {
mysum <- reactiveVal(0)
observeEvent(input$add1, {
mysum(mysum() + 1)
})
observeEvent(input$add5, {
mysum(mysum() + 5)
})
observe({
print(mysum())
})
}
shinyApp(ui, server)
You can read this answer for a bit more in depth discussion