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