Updating non-reactive values in R Shiny - r

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.

Related

How to efficiently subset a dataframe in R Shiny?

In the below example code I reactively subset the mtcars dataframe inside the renderPlot() function. However, in my larger App with many render functions in the server section I am having to repeat the same rv$x[1:input$samples], etc., over and over in many places. How would I apply this subsetting instead "at the top", into the rv <- reactiveValues(...) function itself or equivalent "master function"? I tried subsetting inside the reactiveValues() and got the message "Warning: Error in : Can't access reactive value 'samples' outside of reactive consumer. Do you need to wrap inside reactive() or observer()?" I assumed incorrectly that the reactiveValues() function is a "reactive consumer".
If someone can answer this basic understanding question, please explain the logic for correctly subsetting "at the top" because I am getting very embarrassed by my repeated questions about Shiny reactivity.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
output$p <- renderPlot({plot(rv$x[1:input$samples],rv$y[1:input$samples])})
}
shinyApp(ui, server)
There are multiple ways you can handle this.
Here is one way to create new subset reactive values inside observe.
library(shiny)
ui <- fluidPage(
sliderInput('samples','Nbr of samples:',min=2,max=32,value=16),
plotOutput("p")
)
server <- function(input, output, session) {
rv <- reactiveValues(
x = mtcars$mpg,
y = mtcars$wt
)
observe({
rv$x_sub <- rv$x[1:input$samples]
rv$y_sub <- rv$y[1:input$samples]
})
output$p <- renderPlot({plot(rv$x_sub,rv$y_sub)})
}
shinyApp(ui, server)
I'd use reactiveValues only if you need them to be modified in different places.
reactive is shiny's basic solution for this:
library(shiny)
library(datasets)
ui <- fluidPage(
sliderInput(
'samples',
'Nbr of samples:',
min = 2,
max = 32,
value = 16
),
plotOutput("p")
)
server <- function(input, output, session) {
reactive_mtcars <- reactive({mtcars[1:input$samples,]})
output$p <- renderPlot({
plot(reactive_mtcars()$mpg, reactive_mtcars()$wt)
})
}
shinyApp(ui, server)

R Shiny Debounce with reactiveValues and observeEvent / R input debounce

I am currently trying in vain to use the debounce function in Shiny to delay my input a bit. The goal is to have the renderText not fire every few milliseconds, but only after 2 second intervals.
I tried to implement the following solution. Thereby I absolutely need the reactiveValues and observeEvent functions. Other solutions here never take this combination into account and I am currently stuck. My example code is shortened. In reality the variable name1$data is still used by different functions and the RenderText accesses different variables.
if (interactive()) {
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactiveValues()
observeEvent(input$IText1, {Name1$data <- input$IText1})
#Solutions on stackoverflow
#Just causes errors for me
#Name1$t <- debounce(Name1$data, 2000)
output$OName <- renderText({
Name1$data
})
}
shinyApp(ui, server)
}
Thank you very much for any hint!
Normally we debounce reactive conductors (reactive({......})):
ui <- fluidPage(
textInput("IText1", "Input i want to slow down"),
textOutput("OName")
)
server <- function(input, output, session) {
Name1 <- reactive({
input$IText1
})
Name1_d <- debounce(Name1, 2000)
output$OName <- renderText({
Name1_d()
})
}
shinyApp(ui, server)
EDIT
Or you need
server <- function(input, output, session) {
Name1 <- reactiveValues()
observe({
invalidateLater(2000, session)
Name1$data <- isolate(input$IText1)
})
output$OName <- renderText({
Name1$data
})
}

R Shiny Handsontable automatically updating function after changing a single cell

I am trying to get familiar with the rhandsontable package. So I tried something I thought should be pretty easy but I can't find a solution. Here is the idea:
I am creating a dataframe with random numbers and in a text box. The mean of column 1 of the dataframe should be displayed. Furthermore, that number should be updated as soon as I change the value of a cell in the dataframe.
My code:
ui <- fluidPage(
textOutput("num"),
rHandsontableOutput(outputId="frame")
)
server <- function(input, output, session) {
datavalue <- reactiveValues(data=df)
observeEvent(input$frame$changes$changes,{
mean_col1 <- mean(datavalue$data[[1]][1:10])
})
output$num <- renderText({
mean(datavalue$data[[1]][1:10])
})
output$frame <- renderRHandsontable({
rhandsontable(datavalue$data)
})
}
shinyApp(ui = ui, server = server)
I think you want to use hot_to_r to convert the handsontable to an R object when there is a change. You can update your reactiveValue datavalue$data when that happens, and your output$num will account for this change as well with the new mean.
Try using this in your observeEvent:
datavalue$data <- hot_to_r(input$frame)
As an alternative, you can do a general observe as follows:
observe({
req(input$frame)
datavalue$data <- hot_to_r(input$frame)
})

Shiny - Inserting UI with a for loop returns the same elements in every outputs

I want to insert a non predefined number of graph inside my Shiny App. I use a for loop and a series of insertUI.
Thing is when I run it, the text elements behave as expected, but the graphs are all rendering the same image. How can I prevent that?
Here's a reprex:
library(shiny)
ui <- fluidPage(
tags$div(
class = "this",
actionButton("go", "go")
)
)
server <- function(input, output, session) {
observeEvent( input$go , {
x <- reactiveValues(x = list(iris, mtcars, airquality))
for (i in 1:3){
insertUI(
".this",
ui = tagList(
p(paste("Number", i)),
renderPlot({
plot(x$x[[i]])
})
))
}
})
}
shinyApp(ui, server)
Beware closures in for loops ;). There's no block scope in R, so each for loop iteration shares the same iterator variable i. And the renderXX functions essentially store expressions that aren't evaluated immediately, but only later when it's time to render.
So by the time the plots are ready to render, the for loop is done, i is now 3, and each plot(x$x[[i]]) expression is called as plot(x$x[[3]]).
You can get around this by creating a new scope for each loop iteration using local() or a function. My favorite solution is using lapply as you've found, to run each loop iteration in a function with i as a function-scoped variable.
Many languages without block scope have this same gotcha, like Python and JS:
JavaScript closure inside loops – simple practical example
How do lexical closures work?
So, found the answer to my own question — using lapply() makes this work:
library(shiny)
ui <- fluidPage(
tags$div(
class = "this",
actionButton("go", "go")
)
)
server <- function(input, output, session) {
observeEvent( input$go , {
x <- reactiveValues(x = list(iris, mtcars, airquality))
lapply(1:3, function(i){
insertUI(
".this",
ui = tagList(
p(paste("Number", i)),
renderPlot({
plot(x$x[[i]])
})
))
})
})
}
shinyApp(ui, server)

Passing data within Shiny Modules from Module 1 to Module 2

I dont have a reproducible example as the question is more on how modules work. I am trying to understand how to pass some reactive function from one module to the next. I have received replies in the past about using ObserveEvent but they have not seem to work when I am using the reactive value in one module to perform some other operation in another module
module1 <- function(input, output, session){
data1<-reactive({
#some reacttive funcion that produces an output
})
data2<-reactive({
#some reacttive funcion that produces another output
})
return(list(data1,data2))
}
module2 <- function(input, output, session,data1){
observe( data1(), {
#perform some other functions here using data1().e.g reading or parsing data
})
}
So basically I have a module1 that returns two outputs from data1 and data2
I want to use the value of data1 in module 2 and perform some new operation using that value.
I have looked at other answers to similar questions here but I still dont understand them. If someone can help me explain this concept more clearly that would be of great help
thanks for your help
One possibility is passing the output from one module to the other at construction time. This allows hierachic relationships between modules. There is also the possibility to create memory that is shared between two modules which I will not cover in this answer.
reactiveValues
Here i created an inputModule and an outputModule. The inputModule recieves two textinputs by the user and the output module displays them via verbatimTextOutput. The inputModule passes the user submitted data to the output module as a reactiveValues object called ImProxy (input module proxy). The outputModule accesses the data just like a list (ImProxy$text1, ImProxy$text2).
library(shiny)
inputModuleUI <- function(id){
ns <- NS(id)
wellPanel(h3("Input Module"),
textInput(ns('text1'), "First text"),
textInput(ns('text2'), "Second text"))
}
inputModule <- function(input, output, session){
vals <- reactiveValues()
observe({vals$text1 <- input$text1})
observe({vals$text2 <- input$text2})
return(vals)
}
outputModuleUI <- function(id){
wellPanel(h3("Output Module"),
verbatimTextOutput(NS(id, "txt")))
}
outputModule <- function(input, output, session, ImProxy){
output$txt <- renderPrint({
paste(ImProxy$text1, "&", ImProxy$text2)
})
}
ui <- fluidPage(
inputModuleUI('IM'),
outputModuleUI('OM')
)
server <- function(input, output, session){
MyImProxy <- callModule(inputModule, 'IM')
callModule(outputModule, 'OM', MyImProxy)
}
shinyApp(ui, server)
This approach can be used with observe or observeEvent as well.
list(reactive)
If you want to use reactive rather than reactiveValues, the following adaptiation of the above code can be used. You can leave the ui functions as they are.
inputModule <- function(input, output, session){
list(
text1 = reactive({input$text1}),
text2 = reactive({input$text2})
)
}
outputModule <- function(input, output, session, ImProxy){
output$txt <- renderPrint({
paste(ImProxy$text1(), "&", ImProxy$text2())
})
}
shinyApp(ui, server)
reactive(list)
Again, this will give the same functionality for the app but the proxy pattern is slightly different.
inputModule <- function(input, output, session){
reactive(
list(
text1 = input$text1,
text2 = input$text2
)
)
}
outputModule <- function(input, output, session, ImProxy){
output$txt <- renderPrint({
paste(ImProxy()$text1, "&", ImProxy()$text2)
})
}
shinyApp(ui, server)

Resources