Created
May 5, 2017 17:04
-
-
Save bborgesr/61409e3852feb991336757f06392e52a to your computer and use it in GitHub Desktop.
Two simple examples of iterative printing in Shiny
This file contains 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
# By default Shiny will always wait for any reactive dependencies to finish (re)calculcating | |
# before displaying the output that depends on them. Without async reactivity in Shiny (which | |
# is not currently available, but it's in the works), it's actually not really possible to | |
# make a reactive dependency be available before it finishes computing. But you can redefine | |
# your reactive so that its output is broken though. | |
# | |
# You can achieve this using reactive values, an observer and invalidateLater(). If this is | |
# new for you, I'd recommend, you read the documentation about invalidateLater(). | |
# | |
# I'll show two simple examples here. For a more elaborate example, see this gist. | |
# | |
# The first and probably best approach is to do something like: | |
library(shiny) | |
ui <- fluidPage( | |
verbatimTextOutput("loc") | |
) | |
server <- function(input, output, session) { | |
rv <- reactiveValues(lines = character(0), n = 0) | |
observe({ | |
# Re-execute this reactive expression immediately after | |
# it finishes (though you can also specify a longer time: | |
# e.g. 1000 means re-execute after 1000 milliseconds have | |
# passed since it was last executed) | |
invalidateLater(0, session) | |
isolate({ | |
if (rv$n >= 100) { | |
return() | |
} else { | |
rv$n <- rv$n + 1 | |
rv$lines <- c(rv$lines, paste("Loop var =", rv$n)) | |
} | |
}) | |
}) | |
output$loc <- renderPrint({ | |
rv$lines | |
}) | |
} | |
shinyApp(ui, server) | |
# This will re-execute the observer repeatedly until you've reached rv$n = 100 (actually, | |
# it will keep re-executing forever and it always stops because rv$n will never be | |
# decremented once it reaches 100). So in the first iteration, rv$lines starts as nothing | |
# and then becomes the string "Loop var = 1". It returns that and the renderPrint() call | |
# will print that to the screen. However, immediately after the observer finished, it will | |
# re-start and the rv$lines object will now have two strings: "Loop var = 1" and | |
# "Loop var = 2". It returns that and the renderPrint() call will print that to the screen. | |
# And so on... This happens very fast in the example above, but will be very clear if you | |
# change the line invalidateLater(0, session) to something like a 1/4 of a second: | |
# invalidateLater(250, session). | |
# | |
# This first approach is IMO intuitive and it stays neatly within the reactive framework | |
# that Shiny strongly encourages. However, it does have one drawback: the renderPrint() | |
# call will always print all the strings, each time that rv$lines changes, even though, | |
# you're just adding a line at the end (so ideally you wouldn't have to re-print everything). | |
# This isn't an issue for 100 iterations of printing text, but for larger number or | |
# heavier objects (images, for example), this can become noticeable. | |
# | |
# But there is another option that is fairly new to Shiny, which is to use insertUI(). | |
# This option follows imperative, not reactive, logic, so I'd only recommend it if you feel | |
# like you understand reactivity AND it makes sense for your use case. In particular, this | |
# approach will let you print things one after the other (instead of always printing one | |
# big block), but it loses track of them. If these are values that could change later, or | |
# that you'd like to read in some other place, you shouldn't use this approach. (There are | |
# ways to use insertUI in more complicated ways, but that's another discussion, because I | |
# don't think that's what you need in this post.) This has the advantage of being faster | |
# and not keeping a possible very large set of strings/objects in memory. If this is new | |
# for you, I'd recommend, you read the documentation about insertUI(). | |
library(shiny) | |
ui <- fluidPage( | |
div(id = "placeholder") | |
) | |
server <- function(input, output, session) { | |
rv <- reactiveValues(n = 0) | |
observe({ | |
invalidateLater(0, session) | |
isolate({ | |
if (rv$n >= 100) { | |
return() | |
} else { | |
rv$n <- rv$n + 1 | |
insertUI("#placeholder", ui = p("Loop var = ", rv$n)) | |
} | |
}) | |
}) | |
} | |
shinyApp(ui, server) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mostly copied from my reply here: https://groups.google.com/forum/#!topic/shiny-discuss/0ExyzzaaXTU