Reactive in Shiny

Intro

In preparing an invited talk on Shiny, I organized my experience and notes on reactive programming, and found the storyline I developed may actually be a good alternative compare to the usual tutorials on this topic. Thus I’m expanding the talk slides into a blog post and sharing it here.

Programming for User Interface: Event Driving programming

Programming user interface is different from some other domains, because user interface need to respond to user input and you don’t know when that will happen. Usually this means you write some logic for some possible situations, and there will be a maintained loop watching for user input, and trigger the appropriate logic when the input happens.

In desktop application development, the common pattern is Event Driven programming. User input will generate some event, and the event object have information about the input. You can write code for specific event and conditions, “register” the event to the system (the programming framework), and the system will trigger the code. Here the framework handle the details about event, registering, triggering, and developer only need to write code for event handling.

This pattern is straightforward and not hard to understand. Shiny support this pattern too (observeEvent, note sometimes you may see code examples using observe, which is a low level API and I believe usually there is no real reason for you to use observe instead of more friendly observeEvent.) since it’s a good approach for certain use cases.

There is a slight difference in Shiny observeEvent though. You can think it is observing data changes in the target, not really some event object (it’s possible in the underlying level implementation of Shiny framework something can be called as event object, but I think this way of understanding will help to recognize the difference and connection to the reactive programming topic later). For example, an actionButton click actually just increase its return value by 1, and that value change can trigger some observeEvent code. You can even write something like observeEvent(1, {...}), just the code will only execute once and not again.

If we think observeEvent observe data changes, it can be triggered by any kind of change, including user input (which will change the value input$widget_id), reactive expressions(we will discuss it next).

Summary: observeEvent observe data changes in target expression, run the code once anything changed (there are more options control the fine details, like whether to run in initialization, if to ignore NULL etc, see help page of observeEvent).

observeEvent: data changes ---trigger---> event handling code

Note the official tutorials differentiate event observer and reactive expressions mainly by side effect/calculated values. In my experience this difference is less useful than the difference of source/target of changes, the latter often determined which one you need to use, and you can have side effect in reactive expression in some valid user cases. After all, anything interacting with outside world is side effect, and we need to interact with outside world a lot in user interface programming.

If your reactive expression only returned some changed values and that didn’t reflect to GUI, why were the changes needed? if it did reflect to GUI, that’s still side effect, just shiny framework did the plumbing work and made the changes so the reactive expression didn’t look like did anything imperative.

More relevantly, should use the design principle of cohererant and loose coupling. let related event update together. if you have multiple control for one final value, better use a reactive expression instead of multiple observer.

Another pattern: Reactive programming

For more complete and detailed tutorial on reactive programming, check Hadley’s new book on Shiny.

In this post my perspective is to introduce reactive pattern by comparing with event driving programming.

A reactive expression/value will automatically update itself triggered by data changes in source of changes. This automatical update is handled by Shiny framework, thus require less manual work and appears to be more magical to developers.

Reactive Expression: all reactive values inside become source of changes

observeEvent is triggered by data changes in the target expression, while a reactive expression update is triggered by all data changes in all reactive values inside the expression, and you don’t need to register them explicitly.

reactive({
    ...
    Shiny UI reactive values like input$checkbox
    reactive values defined by reactiveValue()
    other reactive expression()
})
dynamic data 1
dynamic data 2  ==> expression reevaluate
dynamic data 3

Note:

  • Reactive expression look like a function, use like a function. Thus you reference it with () for the updated value, transfer it without () in some other scenarios (like Shiny module) when you are using the expression itself but not going to use the updating value immediately.

Reactive Expression Vs observeEvent

Compare to observeEvent, you can establish multiple -> one data update relationship in reactive expression without explicit registering, thus this is a prefered way if it met all your needs.

In observe) help page, there are some official comparison for these two, mainly focused on:

it doesn’t yield a result and can’t be used as an input to other reactive expressions. Thus, observers are only useful for their side effects (for example, performing I/O).
Another contrast between reactive expressions and observers is their execution strategy. Reactive expressions use lazy evaluation; that is, when their dependencies change, they don’t re-execute right away but rather wait until they are called by someone else. Indeed, if they are not called then they will never re-execute. In contrast, observers use eager evaluation; as soon as their dependencies change, they schedule themselves to re-execute.

All these are definitely valid points, but I think the deciding factor for choosing one of them should be just how you want to arrange the source of changes and eager vs lazy evaluation. With observeEvent you need to be more explicit and have more control, with reactive expression you “let it go” and everything will work smoothly if it fit the pattern.

Reactive Values

One real limit with reactive expression is that you cannot modify its value arbitrarily. It can update when source of changes changed, but always change with same expression. When you need to modify the dynamic data from another source/place/time, you need reactive values.

Thus you have more control and more responsibilities with reactive values

- read reactive value inside reactive expression
  - value change ==> expression reevaluate
- write reactive value inside reactive expression
  - expression reevaluate ==> value updated
- read/write same reactive value inside reactive expression?
  - that will cause an infinite loop

Shiny input/output as reactive special cases

  • input value (input$slider_value) are reactive values driven by user input
    • cannot modify it directly by assignment
    • use update* methods to change UI status
  • output code (renderPlot) create reactive scopes like reactive expression
    • return value used immediately
    • if you need to reuse the value, just create a reactive expression and reference it
  • Error: Operation not allowed without an active reactive context
    • Every reactive value inside a reactive domain (like inside a reactive expression, output code which is reactive domain implicitly) get registered by Shiny framework behind the scene so their changes can be monitored. Thus using a reactive value outside of reactive domain will raise this error.
    • If you do need to inspect the value in debugging, or you want to read the value but don’t want the value update trigger reactive expression reevaluation, you can use isolate.

When more controls are needed

The components above can be used to create sophisticated dynamic systems. However sometimes the order of changes may not be ideal with these rules.

  • One simple case is that your downstream reactive expression/value may not have valid upstream value yet when the app UI is initialized. You can use req to hold off the related UI widget rendering before the upstream value is ready.
  • Sometimes you have multiple widgets updating at the same time driven by some changes, and some widget always update slower, this may cause problems.

    For example, DT is one of my favorite package and I used it extensively in my app, often using the table selection to control other parts of app. When a DT table was updated, the row information will update after the whole table render finish, which is often the slowest one if other widgets are updating at the same time. I may have a plot is depending on some row selection value, so there will be a short time period when the row selection value are not valid and plot will render with the invalid value. Once the table finished update it will be corrected.

    In the beginning I tried to use priority levels to adjust the order, but that seemed never work.

    Instead you can use freezeReactiveValue, which will hold off downstream changes until the last second, so the plot will not render with the invalid value.