How-to: Use global variables
Source:vignettes/how-to/use-global-variables.Rmd
use-global-variables.Rmd
Rhino uses box
to ensure reusable and modular code, both from using packages and using
the scripts within the app itself. Rhino favors the explicit over the
implicit, and a local scope over a global scope.
Sometimes we need global definitions however and storing constants is
the most common reason for that. The suggested way to create global
constants in Rhino is to define and export the constant inside its own
R
script in app/logic
.
# app/logic/constant.R
#' @export
answer <- 42
The global constant can be imported and used in other modules.
# app/logic/another_module.R
box::use(app/logic/constant[answer])
Most of the time, it is sufficient to define a constant and then export and use it throughout the app.
However, there are instances when an object may need to change its value as the app runs. In such cases, Rhino suggests the following ways to handle global variables.
Note: Using global variables can make the app more difficult to manage and understand. If the variable can change its value at any point in the app, future changes to the app must consider how this global variable will be affected. A changing global variable may also make testing difficult because tests may affect the global variables. The tests are no longer independent of each other.
Global Variables
Vanilla R
In R
, global variables live
inside .GlobalEnv
. Global variables can be updated
within a function using <<-
.
# constants.R
answer <- 42
set_answer <- function(new_answer) {
answer <<- new_answer
}
When code is loaded with box::use()
, global variables
live inside the module’s
own immutable environment. Updating global variables with
<<-
will not work.
# app/logic/constants.R
#' @export
answer <- 42
#' @export
set_answer <- function(new_answer) {
answer <<- new_answer
}
Variables in a new environment
To overcome box
’s feature of limiting scope, Rhino
suggests creating a new environment and use that environment to contain
the global variables.
# app/logic/__init__.R
#' @export
global <- new.env()
global$answer <- 42
#' @export
set_answer <- function(new_answer) {
global$answer <- new_answer
}
Session Variables
Rhino suggests using arguments in module servers for explicit handling of session variables or user inputs.
module_ui <- function(id) {
ns <- NS(id)
textOutput(ns("answer"))
}
module_server <- function(id, answer) {
moduleServer(id, function(input, output, session) {
output$answer <- renderText(answer())
})
}
shinyApp(
ui = bootstrapPage(
textInput("answer", "Answer"),
module_ui("module")
),
server = function(input, output, session) {
answer <- reactive(input$answer)
module_server("module", answer)
}
)
However, shiny
has support for session$userData
,
an environment that can store session-specific data.
module_ui <- function(id) {
ns <- NS(id)
textOutput(ns("answer"))
}
module_server <- function(id) {
moduleServer(id, function(input, output, session) {
output$answer <- renderText(session$userData$answer())
})
}
shinyApp(
ui = bootstrapPage(
textInput("answer", "Answer"),
module_ui("module")
),
server = function(input, output, session) {
session$userData$answer <- reactive(input$answer)
module_server("module")
}
)
All modules have access to the variables inside
session$userData
.