1 of 19

Sharing app state between modules

Marcin Dubel�Staff Engineer

2 of 19

Sharing app state between modules

Why?

  1. Modules are not the default approach
  2. Lack of modules complicates maintenance
  3. Appsilon way of modules is not-standard
  • Encourages to use modules
  • Provides materials to learn modules communication
  • Presents the not-standard Appsilon way

3 of 19

  • Modularizing Shiny app code
  • Communication between modules
  • Sharing application state

Table of content

4 of 19

Classics recap

Modularizing Shiny app code

5 of 19

# module

counterButton <- function(id, label = "Counter") {

ns <- NS(id)

tagList(

actionButton(ns("button"), label = label),

verbatimTextOutput(ns("out"))

)

}

counterServer <- function(id) {

moduleServer(

id,

function(input, output, session) {

count <- reactiveVal(0)

observeEvent(input$button, {

count(count() + 1)

})

output$out <- renderText({

count()

})

count

}

)

}

# app

ui <- fluidPage(

counterButton("counter1", "Counter #1"),

)

server <- function(input, output, session) {

counterServer("counter1")

}

shinyApp(ui, server)

6 of 19

# module

counterButton <- function(id, label = "Counter") {

ns <- NS(id)

tagList(

actionButton(ns("button"), label = label),

verbatimTextOutput(ns("out"))

)

}

counterServer <- function(id) {

moduleServer(

id,

function(input, output, session) {

count <- reactiveVal(0)

observeEvent(input$button, {

count(count() + 1)

})

output$out <- renderText({

count()

})

count

}

)

}

# app

ui <- fluidPage(

counterButton("counter1", "Counter #1"),

)

server <- function(input, output, session) {

counterServer("counter1")

}

shinyApp(ui, server)

# app

ui <- fluidPage(

counterButton("counter1", "Counter #1"),

counterButton("counter2", "Counter #2")

)

server <- function(input, output, session) {

counterServer("counter1")

counterServer("counter2")

}

shinyApp(ui, server)

7 of 19

  • Small & Understandable
  • Isolated
  • Reusable
  • Independent

Modularizing Shiny app code

8 of 19

Functional approach

Communication between modules

9 of 19

Communication between modules

10 of 19

varselect_mod_server <- function(input, output, session) {

return(

list(

xvar = reactive({ input$xvar }),

yvar = reactive({ input$yvar })

)

)

}

server <- function(input, output, session) {

ames <- make_ames()

plot1vars <- varselect_mod_server("plot1_vars")

plot2vars <- varselect_mod_server("plot2_vars")

res <- scatterplot_mod_server(

"plots",

dataset = ames,

plot1vars = plot1vars,

plot2vars = plot2vars

)

}

scatterplot_server_mod <- function(input, output, session, dataset, plot1vars, plot2vars) {

plot1_obj <- reactive({

p <- scatter_sales(dataset, xvar = plot1vars$xvar(), yvar = plot1vars$yvar())

return(p)

})

plot2_obj <- reactive({

p <- scatter_sales(dataset, xvar = plot2vars$xvar(), yvar = plot2vars$yvar())

return(p)

})

output$plot1 <- renderPlot({

plot1_obj()

})

output$plot2 <- renderPlot({

plot2_obj()

})

}

11 of 19

  • Well-organized
  • Easy to manage
  • Documentable
  • “Functional”

12 of 19

Object “managers” oriented approach

Sharing application state

13 of 19

Sharing application state

State Manager 1

State Manager 2

14 of 19

# varselect

server <- function(id, variables) {

moduleServer(id, function(input, output, session) {

observeEvent(list(input$xvar, input$yvar), {�

variables$set_vars(input$xvar, input$yvar)�

variables$trigger_plot()

})

})

}

Variables <- R6::R6Class(

classname = "Variables",

public = list(

triggers = reactiveValues(plot = 0),

trigger_plot = function() {

self$triggers$plot <- self$triggers$plot + 1

},

varX = NULL,

varY = NULL,

set_vars = function(varX, varY) {

self$varX <- varX

self$varY <- varY

}

)

)

# main

server <- function(id) {

moduleServer(id, function(input, output, session) {

Variables1 <- Variables$new()

varselect$server("plot1_vars", Variables1)

scatterplot$server("plot1", Variables1)

Variables2 <- Variables$new()

varselect$server("plot2_vars", Variables2)

scatterplot$server("plot2", Variables2)

})

}

# scatterplot

server <- function(id, variables) {

moduleServer(id, function(input, output, session) {

[...]

output$plot <- renderPlot({

variables$triggers$plot

scatter_sales(� dataset,� xvar = variables$varX,� yvar = variables$varY

)

})

})

}

15 of 19

Note! It is NOT global reactiveValues solution!

# server

r_global <- reactiveValues()

schedule_server("schedule", r_global)

place_server("place", r_global)

accommodation_server("accommodation", r_global)

covid_server("covid", r_global)

witnesses_server("witnesses", r_global)

preparations_server("preparations", r_global)

16 of 19

  • Overcomplicated!
  • Not explicit!
  • Too much passed to module!
  • How to even test it?

Benefits come with scale.

And scale will come.

17 of 19

Database�Manager

Data�Manager

User�Manager

Validation�Manager

[...]�Manager

State�Manager

Usual suspects:

18 of 19

Database�Manager

Data�Manager

User�Manager

Validation�Manager

Data�Manager

State�Manager

19 of 19

Thank you!

Marcin Dubel�Staff Engineer