One more version, with a few improvements:
- The
$reactive()
method only creates a reactive expression object once; then it stores the reactive expression so if$reactive()
is called again, it returns the same object. (Note that this is good for most use cases, but not all.) - The
reactiveVal
andreactive
are created only if needed (if someone calls the$reactive()
method). - Removed internal use of
isolate()
. This reduces overhead, and makes it easier to use the class without Shiny if desired.
Person <- R6::R6Class(
"Person",
private = list(
name = "",
reactiveDep = NULL,
reactiveExpr = NULL,
invalidate = function() {
private$count <- private$count + 1
private$reactiveDep(private$count)
invisible()
},
count = 0
),
public = list(
initialize = function(name) {
# Until someone calls $reactive(), private$reactiveDep() is a no-op. Need
# to set it here because if it's set in the definition of private above, it will
# be locked and can't be changed.
private$reactiveDep <- function(x) NULL
private$name <- name
},
reactive = function() {
# Ensure the reactive stuff is initialized.
if (is.null(private$reactiveExpr)) {
private$reactiveDep <- reactiveVal(0)
private$reactiveExpr <- reactive({
private$reactiveDep()
self
})
}
private$reactiveExpr
},
print = function() {
cat("Person:", private$name)
},
changeName = function(newName) {
private$name <- newName
private$invalidate()
},
getName = function() {
private$name
}
)
)
pr <- Person$new("Dean")$reactive()
# The observer accesses the reactive expression
o <- observe({
message("Person changed. Name: ", pr()$getName())
})
shiny:::flushReact()
#> Person changed. Name: Dean
# Note that this is in isolate only because we're running at the console; in
# typical Shiny code, the isolate() wouldn't be necessary.
isolate(pr()$changeName("Newname"))
shiny:::flushReact()
#> Person changed. Name: Newname