I’ve been working on Shiny app at work for the past few months. One of the many frustrating things about Shiny lately has been around buttons. Well, it wasn’t really about buttons, but that’s where it started.

Load libraries

library(shiny)
library(bslib)
library(crul)

Helper function, returned a random UUID from an httpbin server

httpbin_uuid <- function(...) {
  con <- crul::HttpClient$new("https://hb.opencpu.org")
  res <- con$get("uuid")
  jsonlite::fromJSON(res$parse("UTF-8"))$uuid
}

A bslib ui component

ui <- page_sidebar(
  title = "My dashboard",
  sidebar = list(
    actionButton("submit", "Submit"),
    actionButton("reset", "Reset")
  ),
  textInput(inputId = "name", "Your name"),
  textOutput("uuid")
)

Here’s the server part that was giving me trouble. As I said this was an inherited repo, and the server side handling for many buttons was done with eventReactive as below. Using eventReactive meant that button clicks only sometimes triggered the server side code.

server <- function(input, output, session) {
  tmp <- eventReactive(input$submit, {
    httpbin_uuid(input$name)
  })

  output$uuid <- renderText({ tmp() })

  observeEvent(input$reset, {
    updateTextInput(session, "name", "Your name", "")
    output$uuid <- renderText({})
  })
}

Eventually I landed upon switching from eventReactive to observeEvent for a variety of reasons. And tried something like this:

  observeEvent(input$submit, {
    output$uuid <- renderText({
      httpbin_uuid(input$name)
    })
  })

However, keen eyes will notice that this still doesn’t work. The final missing piece was the function isolate. Without isolate the observeEvent handler was being triggered on changes other than just a button click.

  observeEvent(input$submit, {
    output$uuid <- renderText({
      isolate(
      	httpbin_uuid(input$name)
      )
    })
  })

Here’s the entire app with eventReactive that didn’t work:

Click to expand
library(shiny)
library(bslib)
library(crul)

httpbin_uuid <- function(...) {
  con <- crul::HttpClient$new("https://hb.opencpu.org")
  res <- con$get("uuid")
  jsonlite::fromJSON(res$parse("UTF-8"))$uuid
}

ui <- page_sidebar(
  title = "My dashboard",
  sidebar = list(
    actionButton("submit", "Submit"),
    actionButton("reset", "Reset")
  ),
  textInput(inputId = "name", "Your name"),
  textOutput("uuid")
)

server <- function(input, output, session) {
  tmp <- eventReactive(input$submit, {
    httpbin_uuid(input$name)
  })

  output$uuid <- renderText({ tmp() })

  observeEvent(input$reset, {
    updateTextInput(session, "name", "Your name", "")
    output$uuid <- renderText({})
  })
}

shinyApp(ui, server)

And here’s the entire app with obseveEvent and isolate that worked:

Click to expand
library(shiny)
library(bslib)
library(crul)

httpbin_uuid <- function(...) {
  con <- crul::HttpClient$new("https://hb.opencpu.org")
  res <- con$get("uuid")
  jsonlite::fromJSON(res$parse("UTF-8"))$uuid
}

ui <- page_sidebar(
  title = "My dashboard",
  sidebar = list(
    actionButton("submit", "Submit"),
    actionButton("reset", "Reset")
  ),
  textInput(inputId = "name", "Your name"),
  textOutput("uuid")
)

server <- function(input, output, session) {
  observeEvent(input$submit, {
    output$uuid <- renderText({
      isolate(httpbin_uuid(input$name))
    })
  })

  observeEvent(input$reset, {
    updateTextInput(session, "name", "Your name", "")
    output$uuid <- renderText({})
  })
}

shinyApp(ui, server)