mirror of
https://github.com/rstudio/shiny.git
synced 2026-01-29 16:58:11 -05:00
123 lines
3.4 KiB
R
123 lines
3.4 KiB
R
Context <- setRefClass(
|
|
'Context',
|
|
fields = list(
|
|
id = 'character',
|
|
.label = 'character', # For debug purposes
|
|
.invalidated = 'logical',
|
|
.invalidateCallbacks = 'list',
|
|
.flushCallbacks = 'list'
|
|
),
|
|
methods = list(
|
|
initialize = function(label='') {
|
|
id <<- .getReactiveEnvironment()$nextId()
|
|
.invalidated <<- FALSE
|
|
.invalidateCallbacks <<- list()
|
|
.flushCallbacks <<- list()
|
|
.label <<- label
|
|
},
|
|
run = function(func) {
|
|
"Run the provided function under this context."
|
|
env <- .getReactiveEnvironment()
|
|
env$runWith(.self, func)
|
|
},
|
|
invalidate = function() {
|
|
"Invalidate this context. It will immediately call the callbacks
|
|
that have been registered with onInvalidate()."
|
|
if (.invalidated)
|
|
return()
|
|
.invalidated <<- TRUE
|
|
|
|
lapply(.invalidateCallbacks, function(func) {
|
|
func()
|
|
})
|
|
NULL
|
|
},
|
|
onInvalidate = function(func) {
|
|
"Register a function to be called when this context is invalidated.
|
|
If this context is already invalidated, the function is called
|
|
immediately."
|
|
if (.invalidated)
|
|
func()
|
|
else
|
|
.invalidateCallbacks <<- c(.invalidateCallbacks, func)
|
|
NULL
|
|
},
|
|
addPendingFlush = function() {
|
|
"Tell the reactive environment that this context should be flushed the
|
|
next time flushReact() called."
|
|
.getReactiveEnvironment()$addPendingFlush(.self)
|
|
},
|
|
onFlush = function(func) {
|
|
"Register a function to be called when this context is flushed."
|
|
.flushCallbacks <<- c(.flushCallbacks, func)
|
|
},
|
|
executeFlushCallbacks = function() {
|
|
"For internal use only."
|
|
lapply(.flushCallbacks, function(func) {
|
|
withCallingHandlers({
|
|
func()
|
|
}, warning = function(e) {
|
|
# TODO: Callbacks in app
|
|
}, error = function(e) {
|
|
# TODO: Callbacks in app
|
|
})
|
|
})
|
|
}
|
|
)
|
|
)
|
|
|
|
ReactiveEnvironment <- setRefClass(
|
|
'ReactiveEnvironment',
|
|
fields = c('.currentContext', '.nextId', '.pendingFlush'),
|
|
methods = list(
|
|
initialize = function() {
|
|
.currentContext <<- NULL
|
|
.nextId <<- 0L
|
|
.pendingFlush <<- list()
|
|
},
|
|
nextId = function() {
|
|
.nextId <<- .nextId + 1L
|
|
return(as.character(.nextId))
|
|
},
|
|
currentContext = function() {
|
|
if (is.null(.currentContext))
|
|
stop('Operation not allowed without an active reactive context. ',
|
|
'(You tried to do something that can only be done from inside a ',
|
|
'reactive function.)')
|
|
return(.currentContext)
|
|
},
|
|
runWith = function(ctx, func) {
|
|
old.ctx <- .currentContext
|
|
.currentContext <<- ctx
|
|
on.exit(.currentContext <<- old.ctx)
|
|
func()
|
|
},
|
|
addPendingFlush = function(ctx) {
|
|
.pendingFlush <<- c(ctx, .pendingFlush)
|
|
},
|
|
flush = function() {
|
|
while (length(.pendingFlush) > 0) {
|
|
ctx <- .pendingFlush[[1]]
|
|
.pendingFlush <<- .pendingFlush[-1]
|
|
ctx$executeFlushCallbacks()
|
|
}
|
|
}
|
|
)
|
|
)
|
|
|
|
.reactiveEnvironment <- ReactiveEnvironment$new()
|
|
.getReactiveEnvironment <- function() {
|
|
.reactiveEnvironment
|
|
}
|
|
|
|
# Causes any pending invalidations to run.
|
|
flushReact <- function() {
|
|
.getReactiveEnvironment()$flush()
|
|
}
|
|
|
|
# Retrieves the current reactive context, or errors if there is no reactive
|
|
# context active at the moment.
|
|
getCurrentContext <- function() {
|
|
.getReactiveEnvironment()$currentContext()
|
|
}
|