Shiny Reactivity Explaination (using ObserveEvent) - r

I am hoping to get some clarity on Shiny's reactivity behavior using the simplified code below as example.
When y is updated in the app, the graph updates.
When x is updated in the app, the graph does NOT update.
I have read Shiny's tutorials and my understanding is that given that I have wrapped both test() and plot() functions in observeEvent, both parameters should not cause the graph to update when changed.
Can someone help explain the logic behind this?
library(shiny)
test <- function(x){x*2}
shinyServer(function(input, output, session) {
observeEvent(input$run, {
x = test(input$x)
output$distPlot <- renderPlot({
if(input$y){
x = x+2
}
plot(x)
})
})
})
shinyUI(fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("x", "x:", 10),
checkboxInput("y", label = "y", value = FALSE),
actionButton("run", "run")
),
mainPanel(
plotOutput("distPlot")
)
)
))

If you put the line x = test(input$x) inside of the renderPlot it will react when either x or y changes. Essentially the observer creates a reactive output when the action button is clicked the first time, then you simply have a reactive element that responds to changes to inputs inside of it. Hope that helps.
To make it so the graph only updates when the button is clicked, you will probably need to put the data that is being graphed in a eventReactive and use that as the input for the graph.
Something like this:
data <- eventReactive(input$run, {
x = test(input$x)
if(input$y){
x = x+2
}
x
})
output$distPlot <- renderPlot({
plot(data())
})

Related

Problem when plotting points on shiny R with eventReactive

This is my first post here so I hope I can explain my problem clearly. I am currently getting into shiny and want to start off by doing basic stuff. I decided to challenge myself by making an app that takes two numeric inputs and plots them whenever I press an actionbar. The problem is that I want to keep the previous points that I plot. I can't get it to work as it keeps resetting the plot. I've tried many different ways and I dont really know how points() works on shiny. Here is the code:
library(shiny)
ui <- fluidPage(
actionButton(inputId="execute",label="Execute"),
numericInput(inputId="numY",label="Y",value=0),
numericInput(inputId="numX",label="X",value=0),
plotOutput("plot")
)
server <- function(input, output) {
coordx <- eventReactive(input$execute,{input$numX})
coordy <- eventReactive(input$execute,{input$numY})
if(!exists("input$execute"))
{
output$plot <- renderPlot({
plot(x=coordx(),y=coordy())
})
}
else
output$plot <- renderPlot({
points(x=coordx(),y=coordy())
})
}
shinyApp(ui = ui, server = server)
Thank you in advance!
As the commenters #Limey and #fvall said, the issue seems to be the eventReactive() which overwrites each time. What I did instead is place the x and y coordinates in a reactiveValues(). Then I placed an observeEvent() for any time input$execute was pressed, writing the x and y coordinates to update the reactiveValues(). This will keep both the old and new values. I also added a little tableOutput() just to keep track of the values:
library(shiny)
ui <- fluidPage(
actionButton(inputId="execute",label="Execute"),
numericInput(inputId="numY",label="Y",value=0),
numericInput(inputId="numX",label="X",value=0),
plotOutput("plot"),
tableOutput("TABLE")
)
server <- function(input, output) {
coord<-reactiveValues("x" = NULL, "y" = NULL)
observeEvent(input$execute, {
req(input$numY, input$numX)
tempx<-c(isolate(coord$x), input$numX)
tempy<-c(isolate(coord$y), input$numY)
coord$x <- tempx
coord$y <- tempy
})
output$plot <- renderPlot({
req(input$execute)
plot(x=isolate(coord$x),y=isolate(coord$y))
})
output$TABLE<-renderTable({
data.frame("x" = coord$x, "y" = coord$y)
})
}
shinyApp(ui = ui, server = server)
Best of luck! Though I had hiccups along the way, I really enjoyed learning Shiny myself, and I hope you do too!

In R Shiny, how can you render outputs outside an observer where their names are reactives?

I have a Shiny app where I have a dynamically created tabsetPanel where each tab contains a table. I do not know how many tabs/tables will be created in each session by users. I understand that it is bad practice to put render* functions inside observe or observeEvent calls but I can't think of any other way to do this. A minimal example of what I'm trying to do is shown below, which just picks a data set randomly to display on a given tab. Essentially, I'm trying to figure out how to call my table renderers without putting them inside an observe. More generally, although I have read it is bad practice to do this, I would also appreciate an explanation of exactly why it's not a good thing to do:
library(shiny)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
numericInput("tabs", "Number of tabs", value = 5),
),
mainPanel(
uiOutput("mytabset")
)
)
)
server <- function(input, output) {
output$mytabset <- renderUI({
mytabs <- lapply(seq_len(input$tabs), function(x) {
tabPanel(
paste("Tab", x),
tableOutput(paste0("tab", x))
)
})
do.call(tabsetPanel, mytabs)
})
observe({
set.seed(1)
lapply(seq_len(input$tabs), function(x) {
output[[paste0("tab", x)]] <- renderTable({
sample(list(mtcars, iris, trees, cars), 1)
})
})
})
}
shinyApp(ui = ui, server = server)
I haven't used them in a while, but I think if you use modules, you can call them from outside of a reactive context, and won't need an observe..? :)

clicking same plotly marker twice does not trigger events twice

I am using Plotly's event_data("plotly_click") to do stuff (opening a modal) after the user clicked on a marker in a scatter plot. Afterwards (e.g. closing the modal), event_data("plotly_click") does of course not change and clicking on the same marker therefore does not trigger the same action again.
Minimal example:
library(plotly)
ui <- fluidPage(
plotlyOutput("plot")
)
server <- function(input, output, session) {
output$plot <- renderPlotly({
mtcars %>% plot_ly(x=~disp, y=~cyl)
})
# Do stuff after clicking on a marker in the plot
observeEvent(event_data("plotly_click"), {
print("do some stuff now") # this is not executed after second click on same marker
})
}
shinyApp(ui, server)
I have tried workarounds with shinyjs's onclick, to no avail (it works well in empty areas of the plot but not when clicking on markers):
shinyjs::onclick(id="plot", print("clicked"))
I have also tried using a reactive Value that stores the last click and is reset immediately afterwards (e.g. by event_data("plotly_hover")), but all tries fail because event_data("plotly_click") remains in its old value.
Can anyone help?
[Edit: The issue has been fixed in Plotly 4.9.0. See answer below. This answer works up to Plotly 4.8.0. From plotly 4.9.0., delete the string .clientValue- from the source code or use below answer.]
I finally solved this issue. I know this is bothering some people, so I'll post my solution here:
Basically I use shinyjs package to reset the data about the last click (the source where event_data("plotly_click") gets its information from) on a certain event (a button in my case).
The definition of the function is (note that "A" needs to be replaced with plotly-source string if used):
extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }")
Then this is called upon button click by js$resetClick().
Minimal example:
library(shiny)
library(plotly)
library(shinyjs)
ui <- shinyUI(
fluidPage(
useShinyjs(),
# code to reset plotlys event_data("plotly_click", source="A") to NULL -> executed upon action button click
# note that "A" needs to be replaced with plotly source string if used
extendShinyjs(text = "shinyjs.resetClick = function() { Shiny.onInputChange('.clientValue-plotly_click-A', 'null'); }"),
actionButton("reset", "Reset plotly click value"),
plotlyOutput("plot"),
verbatimTextOutput("clickevent")
)
)
server <- shinyServer(function(input, output) {
output$plot <- renderPlotly({
plot_ly(mtcars, x=~cyl, y=~mpg)
})
output$clickevent <- renderPrint({
event_data("plotly_click")
})
observeEvent(input$reset, {
js$resetClick()
})
})
shinyApp(ui, server)
The issue has finally been fixed on Plotly side: https://github.com/ropensci/plotly/issues/1043
event_data("plotly_click", priority = "event") updates on every click, not only on shiny input change (as before). Working from Plotly 4.9.0 on.
Minimal example using Plotly 4.9.0:
library(shiny)
library(plotly)
ui <- shinyUI(
fluidPage(
plotlyOutput("plot", height = 200),
verbatimTextOutput("time_last_click")
)
)
server <- shinyServer(function(input, output) {
output$plot <- renderPlotly({
plot_ly(mtcars[1,], x=~cyl, y=~mpg, size = 1)
})
output$time_last_click <- renderPrint({
tmp <- event_data("plotly_click", priority = "event")
Sys.time()
})
})
shinyApp(ui, server)
I had the same problem, and came up with a solution where I specified the source argument of the plotly object to be a reactive value as follows:
In plot_ly(data,x,y,...,source = x) and event_data(...,source = x) let x be an element of a reactiveValues object. When your event triggers, change the value of x (increment or hash), which instantiates a new event_data() object.
Worked like a charm.

Print console text output in shiny synchronously

I recently ran into a problem that as my computing took lots of time, I'd like to show the text output on shiny rather than progress bar or loading message. My function looked like
printText <- function() {
for(i in 1:10){
Sys.sleep(0.1)
print(paste("My text", i))
y = i + 1
}
return(y)
}
I can print it with verbatimTextOutput but I also need the returned value of y. Now I am doing this:
runApp(list(
ui = shinyUI(fluidPage(
titlePanel("Print consol output"),
sidebarLayout(
sidebarPanel(actionButton("go", "Go")),
mainPanel(
verbatimTextOutput("text")
)
)
)),
server = shinyServer(function(input, output, session){
observeEvent(input$go, {
output$text <- renderPrint({
y <- printText()
})
})
})
))
The problem is that if I want to use the returned y I need to create a reactive object, which may take me 2 times longer because I execute printText() twice, while printing and pass to reactive object.
How could I get the value of y and print the text onto shiny without duplicated work? Notice that I'm not gonna use progress bar because my real function is not loop actually. What I want is to capture the text output during the process and get returned value.
You can do this way :
ui.r
textOutput("id")
server.r
output$id<-renderText(input$y)

Can I let Shiny wait for a longer time for numericInput before updating?

In my Shiny App, there are a few numericInput and selectInput.
Shiny updates outputs during typing, especially when users type is slower in the numericInput.
sumbitButton could you be used to stop automatically updading. But I prefer to not to use it.
How could I let Shiny waits for a longer time for numericInput?
Thanks for any suggestion. Let me know if my question is not clear.
You can use debounce on the reactive function that uses your Inputs.
Setting it to 2000 milliseconds felt OK to me.
If you use the input directly in a render function you might need to create the data to use in your render function in a reactive function.
An example is here: https://shiny.rstudio.com/reference/shiny/latest/debounce.html
## Only run examples in interactive R sessions
if (interactive()) {
options(device.ask.default = FALSE)
library(shiny)
library(magrittr)
ui <- fluidPage(
plotOutput("plot", click = clickOpts("hover")),
helpText("Quickly click on the plot above, while watching the result table below:"),
tableOutput("result")
)
server <- function(input, output, session) {
hover <- reactive({
if (is.null(input$hover))
list(x = NA, y = NA)
else
input$hover
})
hover_d <- hover %>% debounce(1000)
hover_t <- hover %>% throttle(1000)
output$plot <- renderPlot({
plot(cars)
})
output$result <- renderTable({
data.frame(
mode = c("raw", "throttle", "debounce"),
x = c(hover()$x, hover_t()$x, hover_d()$x),
y = c(hover()$y, hover_t()$y, hover_d()$y)
)
})
}
shinyApp(ui, server)
}

Resources