Make an object respond only to specified reactive events — bindEvent

v1.10.0|Source: R/bind-event.R

Description

Modify an object to respond to "event-like" reactive inputs, values, and expressions. bindEvent() can be used with reactive expressions, render functions, and observers. The resulting object takes a reactive dependency on the ... arguments, and not on the original object's code. This can, for example, be used to make an observer execute only when a button is pressed.

bindEvent() was added in Shiny 1.6.0. When it is used with reactive() and observe(), it does the same thing as eventReactive() and observeEvent(). However, bindEvent() is more flexible: it can be combined with bindCache(), and it can also be used with render functions (like renderText() and renderPlot()).

bindEvent(
  x,
  ...,
  ignoreNULL = TRUE,
  ignoreInit = FALSE,
  once = FALSE,
  label = NULL
)

Arguments

x

An object to wrap so that is triggered only when a the specified event occurs.

...

One or more expressions that represents the event; this can be a simple reactive value like input$click, a call to a reactive expression like dataset(), or even a complex expression inside curly braces. If there are multiple expressions in the ..., then it will take a dependency on all of them.

ignoreNULL

Whether the action should be triggered (or value calculated) when the input is NULL. See Details.

ignoreInit

If TRUE, then, when the eventified object is first created/initialized, don't trigger the action or (compute the value). The default is FALSE. See Details.

once

Used only for observers. Whether this observer should be immediately destroyed after the first time that the code in the observer is run. This pattern is useful when you want to subscribe to a event that should only happen once.

label

A label for the observer or reactive, useful for debugging.

Details

Shiny's reactive programming framework is primarily designed for calculated values (reactive expressions) and side-effect-causing actions (observers) that respond to any of their inputs changing. That's often what is desired in Shiny apps, but not always: sometimes you want to wait for a specific action to be taken from the user, like clicking an actionButton(), before calculating an expression or taking an action. A reactive value or expression that is used to trigger other calculations in this way is called an event.

These situations demand a more imperative, "event handling" style of programming that is possible--but not particularly intuitive--using the reactive programming primitives observe() and isolate(). bindEvent() provides a straightforward API for event handling that wraps observe and isolate.

The ... arguments are captured as expressions and combined into an event expression. When this event expression is invalidated (when its upstream reactive inputs change), that is an event, and it will cause the original object's code to execute.

Use bindEvent() with observe() whenever you want to perform an action in response to an event. (This does the same thing as observeEvent(), which was available in Shiny prior to version 1.6.0.) Note that "recalculate a value" does not generally count as performing an action -- use reactive() for that.

Use bindEvent() with reactive() to create a calculated value that only updates in response to an event. This is just like a normal reactive expression except it ignores all the usual invalidations that come from its reactive dependencies; it only invalidates in response to the given event. (This does the same thing as eventReactive(), which was available in Shiny prior to version 1.6.0.)

bindEvent() is often used with bindCache().

ignoreNULL and ignoreInit

bindEvent() takes an ignoreNULL parameter that affects behavior when the event expression evaluates to NULL (or in the special case of an actionButton(), 0). In these cases, if ignoreNULL is TRUE, then it will raise a silent validation error. This is useful behavior if you don't want to do the action or calculation when your app first starts, but wait for the user to initiate the action first (like a "Submit" button); whereas ignoreNULL=FALSE is desirable if you want to initially perform the action/calculation and just let the user re-initiate it (like a "Recalculate" button).

bindEvent() also takes an ignoreInit argument. By default, reactive expressions and observers will run on the first reactive flush after they are created (except if, at that moment, the event expression evaluates to NULL and ignoreNULL is TRUE). But when responding to a click of an action button, it may often be useful to set ignoreInit to TRUE. For example, if you're setting up an observer to respond to a dynamically created button, then ignoreInit = TRUE will guarantee that the action will only be triggered when the button is actually clicked, instead of also being triggered when it is created/initialized. Similarly, if you're setting up a reactive that responds to a dynamically created button used to refresh some data (which is then returned by that reactive), then you should use reactive(...) %>% bindEvent(..., ignoreInit = TRUE) if you want to let the user decide if/when they want to refresh the data (since, depending on the app, this may be a computationally expensive operation).

Even though ignoreNULL and ignoreInit can be used for similar purposes they are independent from one another. Here's the result of combining these:

ignoreNULL = TRUE and ignoreInit = FALSE

This is the default. This combination means that reactive/observer code will run every time that event expression is not NULL. If, at the time of creation, the event expression happens to not be NULL, then the code runs.

ignoreNULL = FALSE and ignoreInit = FALSE

This combination means that reactive/observer code will run every time no matter what.

ignoreNULL = FALSE and ignoreInit = TRUE

This combination means that reactive/observer code will not run at the time of creation (because ignoreInit = TRUE), but it will run every other time.

ignoreNULL = TRUE and ignoreInit = TRUE

This combination means that reactive/observer code will not at the time of creation (because ignoreInit = TRUE). After that, the reactive/observer code will run every time that the event expression is not NULL.

Types of objects

bindEvent() can be used with reactive expressions, observers, and shiny render functions.

When bindEvent() is used with reactive(), it creates a new reactive expression object.

When bindEvent() is used with observe(), it alters the observer in place. It can only be used with observers which have not yet executed.

Combining events and caching

In many cases, it makes sense to use bindEvent() along with bindCache(), because they each can reduce the amount of work done on the server. For example, you could have sliderInputs x and y and a reactive() that performs a time-consuming operation with those values. Using bindCache() can speed things up, especially if there are multiple users. But it might make sense to also not do the computation until the user sets both x and y, and then clicks on an actionButton named go.

To use both caching and events, the object should first be passed to bindCache(), then bindEvent(). For example:

r <- reactive({
   Sys.sleep(2)  # Pretend this is an expensive computation
   input$x * input$y
 }) %>%
 bindCache(input$x, input$y) %>%
 bindEvent(input$go)

Anything that consumes r() will take a reactive dependency on the event expression given to bindEvent(), and not the cache key expression given to bindCache(). In this case, it is just input$go.