Anatomy of a Shiny app

Shiny in Production 2023

Chris Brownlie

Barnett Waddingham

13th Oct 2023

About me

  • Chris Brownlie
  • Analytics consultant @ Barnett Waddingham
  • 5 years with R & shiny
  • Expertise on {shiny} and processes

It is a weird title

  • Anatomy of a shiny app AKA a high level look at the {shiny} source code
  • This will be theoretical, but bear with me - it will be worth it!
  • What I won’t be talking about:
    • javascript & websockets
    • UI design
    • deployment/infrastructure

What do you mean ‘anatomy’?

  • Visible = UI
  • Brain = Server
  • Organs = {shiny} and friends

My hopes and dreams (for this talk)

  • Better mental model
  • De-magic-ify
  • Improve app design

Pre-requisite: R6

  • {R6} provides a method of OOP
  • Important for {shiny}
  • Reference semantics

A subjective classification

Shiny R6 classes

When are they created

  • On load: RLog, TimerCallbacks, HandlerManager
  • In runApp(): ShinySession, Bookmarking classes
  • In the app event loop: ReactiveEnvironment, Core Helpers
  • User-dependent (mostly):
    • ReactiveVal (reactiveVal())
    • ReactiveValues (reactiveValues())
    • Observers (observe(), observeEvent(), render_X())
    • Observable (reactive())

ShinySession

  • You might recognise: session
  • Contains instances of most helper/core helper classes
  • Important tasks
    • Communicates with browser via websockets
    • Handles outputs in the server
    • Initiates and manages ‘cycles’

ReactiveEnvironment & Context

  • ReactiveEnvironment keeps track of Contexts
  • Contexts can be invalidated (notify the ReactiveEnvironment they need to be ‘flushed’)
  • Contexts can be flushed (the function is run)

ReactiveVal & ReactiveValues

  • You might recognise: reactiveVal() and reactiveValues()
  • Distinct but similiar
  • Keep track of value, label and dependents

Observer

  • You might recognise: observe()/observeEvent()
  • Handling for invalidation (a non-isolated reactive value changes)
  • Handling for flushes (app cycle, values have been recalculated)

Observable

  • You might recognise: reactive()
  • Turns an expression into a function
  • Notifies dependents

What is the point of all this?

  • Some common design mistakes
  • reactive vs observe
    • i.e. ReactiveVal/ues vs Observable vs Observer
  • Overly reactive expressions
    • Understanding the reactive graph
  • Misunderstanding modules
    • ShinySession, namespaces and input-output interactions

To conclude

  • Might seem unimportant, hidden for a reason
  • Mental models are hard
  • Understanding the tools we use is good

Just for fun

My favourite comments from the {shiny} source code

(AKA shiny devs are like you and me)