0

In shiny apps, it can be difficult to use iterative self-referring.

This caused me a great deal of head scratching, but ...

Søren ONeill
  • 355
  • 2
  • 13

2 Answers2

0

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.

Søren ONeill
  • 355
  • 2
  • 13
0

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

DeanAttali
  • 25,268
  • 10
  • 92
  • 118
  • Very useful reply - thank you very much. I did previously try using observe but found my app got gradually slower and slower (a memory leak I suspect). Perhaps the problem was elsewhere in my code ... I'l certainly try again. I had actually come across your link before and the video with Cheng. – Søren ONeill Feb 04 '19 at 16:25