In shiny apps, it can be difficult to use iterative self-referring.
This caused me a great deal of head scratching, but ...
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
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.
Related
I have the following example:
library(shiny)
ui <- fluidPage(
textOutput("out"),
actionButton("plusX", "Increase X"),
actionButton("redraw", "redraw")
)
server <- function(input, output, session) {
x <- 0
observeEvent(input$plusX, {x <<- x+1})
output$out <- renderText({
input$redraw
x
})
}
shinyApp(ui, server)
Is this considered an anti-pattern in Shiny to modify a non-reactive variable in this way? Obviating the super assignment which can be problematic by itself.
I know this could be done, for example with a reactiveVal to store X, and isolate to obtain a similar result. This second way seems clearer and that would be my usual choice, but I was wondering if there any caveats in the first one, or it is possible way of doing that.
library(shiny)
ui <- fluidPage(
textOutput("out"),
actionButton("plusX", "Increase X"),
actionButton("redraw", "redraw")
)
server <- function(input, output, session) {
x <- reactiveVal(0)
observeEvent(input$plusX, {x(x()+1)})
output$out <- renderText({
input$redraw
isolate(x())
})
}
shinyApp(ui, server)
In this example there is no important difference between both codes as you are not using the benefit of ReactiveVal.
The benefit of ReactiveVal is that it has a reactive nature and thus can interact with other reactive elements.
Try for example to add a table to your code that depends on x:
output$tab <- renderTable({data.frame(y = x)})
(x() in the case of ReactiveVal)
The difference you will see that in the case of ReactiveVal the table automatically updates with plusX whereas in the case of the regular variable it does not update.
I am working with a very large RShiny app and want to take advantage of code folding to organize the server.R file in this application. However, when I use the code-fold hotkey, it does not fold the various elements defined in the server (the reactive, render, etc. elements).
I'd like to be able to take this
# observe some things
observe({
query <- parseQueryString(session$clientData$url_search)
if (!is.null(query[['tab']])) {
updateTabItems(session, "sidebarMenu", selected = query[['tab']])
}
if (!is.null(query[['player']])) {
updateSelectInput(session, "profile", selected = query[['player']])
}
})
# Lots of "reactive" data fetching functions
league_stats <- reactive({
get1 <- fetch('yada')
return(get1)
})
# another reactive
shooting <- reactive({
get1$SHORT_MR_MADE<-sum(get1$short_mr_fgm,na.rm=T)
...
...
)}
and collapse it into this (or something like this) by just hitting the code-collapse hotkey.
# observe some things
observe({--})
# Lots of "reactive" data fetching functions
league_stats <- reactive({--})
# another reactive
shooting <- reactive({--})
Is this possible to do with R / RStudio? I would like to avoid using the 4 # signs #### above the function to code fold, as this will hide the shooting <- reactive({--}) strings as well, however I'd like to still have show (and just hide the code inside).
I will oftentimes wrap code in functions since functions collapse, however I cannot wrap RShiny reactive elements in functions (or, i'm not sure how), as it seems like this breaks the app.
Shiny reactives behave as other functions, but you need to take care about passing to them the input, session or other reactives (as function, not as value) they need.
As an illustration :
library(shiny)
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
Collapsed code :
Another way to do this without creating them as functions is to put an identifier above each code chunk:
library(shiny)
# Generate UI ----
generateUI <- function() {fluidPage(
actionButton("do", "Click Me"),
textOutput('counter')
)}
ui <- generateUI()
# Observer ----
myobserver <- function(input,counter) {
observeEvent(input$do, {
cat('Clicked \n')
counter(counter()+1)
})
}
# Formatter ----
myformater <- function(counter) {
renderText(paste('count is',counter()))
}
# Server ----
server <- function(input, output, session) {
counter <- reactiveVal(0)
myobserver(input,counter)
output$counter <- myformater(counter)
}
shinyApp(ui, server)
You will then be able to collapse code segments in between the two identifiers to view as shown below:
I would like to create a loop that generates as many outputs as the user chooses.
for example, if the user chooses 10 models, 10 outputs of those 10 models will come out.
Note: I know that inputs cannot be used within the UI. I just use it as a reference to make it clearer to understand what I am trying to do.
Here is what I've tried:
shinyApp(ui= fluidPage(checkboxGroupInput('aa','model',c('a','b','c'), 'a'),
for(i in 1:length(input$aa)){
function(i){uiOutput(paste0('prob', i))}
}),
server = function(input, output){
for(i in input$aa){
assign(paste('output$prob',i,sep = ''),renderUI({paste('model',i)}))
}
})
Thanks.
In shiny, for loops don't work well, it's better to use lapply. Below you find a solution with lapply, you could also take a look at insertUI/removeUI
library(shiny)
ui <- fluidPage(checkboxGroupInput('aa','model',c('a','b','c'), 'a'),
uiOutput("formulas")
)
server <- function(input, output, session) {
output$formulas <- renderUI({
lapply(input$aa, function(model) {
verbatimTextOutput(paste0("prob_", model))
})
})
observeEvent(input$aa, {
lapply(input$aa, function(model) {
output[[paste0("prob_", model)]] <- renderPrint({paste0("model ", model)})
})
})
}
shinyApp(ui, server)
I'm new to Shiny and have hit a problem I can't find an answer for. Basically, I have a Shiny app that does some long calculations in a loop and I want it to output a "progress report" every few iterations. However, even though I reassign my reactive variable within the loop, the output doesn't update until the loop (or entire function?) has finished.
Here is a simplified test case of what I mean:
library(shiny)
# Basic interface
ui <- fluidPage(
actionButton("run", "Run"),
textOutput("textbox")
)
# Basic server with loop
server <- function(input, output) {
textvals=reactiveValues(a=0)
observeEvent(input$run, {
for(i in 1:10){
textvals$a=i # Expect output to update here, but doesn't
Sys.sleep(0.1) # Slight pause so isn't instantaneous
}
})
output$textbox <- renderText({
textvals$a
})
}
# Run the application
shinyApp(ui = ui, server = server)
What I would expect is that the display would update 1, 2, 3, ... 10 as the loop executes. Instead, it just jumps straight from 0 to 10. How can I force an update partway through the loop?
Thank you.
With using invalidateLater you can get something closed to what you want. Not the shortest way to do it I think, but it may help you to find a better solution.
library(shiny)
# Basic interface
ui <- fluidPage(
actionButton("run", "Run"),
textOutput("textbox")
)
# Basic server with loop
server <- function(input, output, session) {
textvals <- reactiveVal(0)
active <- reactiveVal(FALSE)
output$textbox <- renderText({
textvals()
})
observe({
invalidateLater(1000, session)
isolate({
if (active()) {
textvals(textvals() + 1)
if (textvals() > 9) {
active(FALSE)
}
}
})
})
observeEvent(input$run, {
active(TRUE)
})
}
# Run the application
shinyApp(ui = ui, server = server)
By the way, reactive and for loops don't really get on well. This may help : https://gist.github.com/bborgesr/e1ce7305f914f9ca762c69509dda632e
I'm trying to get a reactiveValue that is depending on a reactive. In the real code (this is a very simplified version), I load a dataset interactively. It changes when pushing the buttons (prevBtn/nextBtn). I need to know the number of rows in the dataset, using this to plot the datapoints with different colors.
The question: Why can't I use the reactive ro() in the reactiveValues function?
For understanding: Why is the error saying "You tried to do something that can only be done from inside a reactive expression or observer.", although ro() is used inside a reactive context.
The error is definitely due to vals(), I already checked the rest.
The code :
library(shiny)
datasets <- list(mtcars, iris, PlantGrowth)
ui <- fluidPage(
mainPanel(
titlePanel("Simplified example"),
tableOutput("cars"),
actionButton("prevBtn", icon = icon("arrow-left"), ""),
actionButton("nextBtn", icon = icon("arrow-right"), ""),
verbatimTextOutput("rows")
)
)
server <- function(input, output) {
output$cars <- renderTable({
head(dat())
})
dat <- reactive({
if (is.null(rv$nr)) {
d <- mtcars
}
else{
d <- datasets[[rv$nr]]
}
})
rv <- reactiveValues(nr = 1)
set_nr <- function(direction) {
rv$nr <- rv$nr + direction
}
observeEvent(input$nextBtn, {
set_nr(1)
})
observeEvent(input$prevBtn, {
set_nr(-1)
})
ro <- reactive({
nrow(dat())
})
output$rows <- renderPrint({
print(paste(as.character(ro()), "rows"))
})
vals <- reactiveValues(needThisForLater = 30 * ro())
}
shinyApp(ui = ui, server = server)
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
I think you want
vals <- reactiveValues(needThisForLater = reactive(30 * ro()))
Not everything in a reactiveValues list is assumed to be reactive. It's also a good place to store constant values. So since it's trying to evaluate the parameter you are passing at run time and you are not calling that line in a reactive environment, you get that error. So by just wrapping it in a call to reactive(), you provide a reactive environment for ro() to be called in.