Chapter 7 User feedback

Important to respond to the user as they interact with your app. In this chapter we’ll show how you can dynamically push feedback out of R and into the browser.

Many of the techniques here are best described as side-effects so that they can appear only at the end of the reactive graph.

7.1 Validation

7.1.1 Ignore missing inputs

Most frequent offender is textInput(), because it starts off blank. Can also use with inputSelect() with a choice of "". Another important use case is with fileInput(), see Section @ref(#upload) for more details.

The easiest way to fix this problem is with req(): it checks that the input has been set before proceeding. req() sends a special signal to Shiny telling it that the reactive isn’t ready yet18

You can use req() inside of an output, but it’s good practice to use inside a reactive() because it isolates computation in one place.

Truthi-ness. req() is designed so that req(input$x) will do what you expect regardless of what type of input x is. You can see the details in ?isTruthy

7.1.2 Validate input

You can extend the same technique to provide other feedback.

validate()

With tryCatch()

7.2 Notifications

7.2.1 Progress

  • Can divide the task into units that take roughly the same amount of time.
  • Can divide the task into discrete steps

Progress bar. Simplest way is to use withProgress()

If you need more control, see the details in ?Progress and https://shiny.rstudio.com/articles/progress.html.

With progress package - i.e. forwarding the conditions it generates to withProgress(). Should be able to make simple wrapper.

7.2.2 Message

If you don’t know how long some code will take, a better approach is to use notifications.

Two important ideas here:

  • We captured the notification id created by showNotification().

  • We use on.exit() to automatically cancel the notification when the reactive complete, regardless of whether it returns a value or throws an error.

Also note the use of duration = NULL and closeButton = FALSE that makes the notification most suitable for this task, ensuring that is stays visibile until the data loading is done.

(For this specific case you should also look at data.table::fread() and vroom::vroom() to read in the file; they can be orders of magnitude faster than read.csv(). And pointer to chapter about performance/promises/future)

You can slightly extend this approach to send multiple notifications if there are discrete steps (where you also don’t know how long they’ll take)


  1. Technically, it leaves any downstream reactive consumers in an invalidated state. We’ll come back to this terminology in Chapter @ref{reactive-components}.