R6 class reactiveValues property and instantiation

Hi,

I'm looking at an issue with using reactiveValues as an R6 class property. It seems the values are shared between instances, i.e. I can instantiate two objects, but the reactiveValues class property has the same environment. Changing one instance property thus changes the other instance's property.
I found a workaround unlocking the R6 class environments, but I am not sure this is the best way to achieve proper instantiation. Is there are way to manually control the reactiveValues instantiation/environment?

I added two minimum examples:

Shared property:

library(shiny)
library(R6)

testBaseModel <- R6Class("testBaseModel",
                         public = list(
                           nonReactiveVar = FALSE,
                           reactiveVar = reactiveValues(bTest = FALSE)
                         )
)

testModel <- R6Class("testModel",
                     inherit = testBaseModel,
                     public = list(
                     )
)

ui <- function() {
  fluidPage(
    actionButton("button", "button"),
    textOutput("instanceA"),
    textOutput("instanceB")
  )
}

server <- function(input, output, session) {
  modelInstanceA <- testModel$new()
  modelInstanceB <- testModel$new()

  observeEvent(input$button, {
    modelInstanceA$reactiveVar$bTest <- TRUE
    modelInstanceA$nonReactiveVar <- TRUE

    # changes both instances
    output$instanceA <- renderText({
      paste(modelInstanceA$reactiveVar$bTest, modelInstanceA$nonReactiveVar)
    })
    output$instanceB <- renderText({ 
      paste(modelInstanceB$reactiveVar$bTest, modelInstanceB$nonReactiveVar)
    })
  })
}

shinyApp(ui, server)

Isolated property

library(shiny)
library(R6)

testBaseModel <- R6Class("testBaseModel",
                         lock=FALSE,
                         public = list(
                           nonReactiveVar = FALSE,
                           initialize = function() {
                            self[["reactiveVar"]] = reactiveValues(bTest = FALSE)
                           }
                         )
)

testModel <- R6Class("testModel",
                     inherit = testBaseModel,
                     lock=FALSE,
                     public = list(
                     )
)

ui <- function() {
  fluidPage(
    actionButton("button", "button"),
    textOutput("instanceA"),
    textOutput("instanceB")
  )
}

server <- function(input, output, session) {
  modelInstanceA <- testModel$new()
  modelInstanceB <- testModel$new()

  observeEvent(input$button, {
    modelInstanceA$reactiveVar$bTest <- TRUE
    modelInstanceA$nonReactiveVar <- TRUE

    # changes both instances?
    output$instanceA <- renderText({
      paste(modelInstanceA$reactiveVar$bTest, modelInstanceA$nonReactiveVar)
    })
    output$instanceB <- renderText({ 
      paste(modelInstanceB$reactiveVar$bTest, modelInstanceB$nonReactiveVar)
    })
  })
}

shinyApp(ui, server)

Thanks,
Christian

I would do a combination of both methods without the lock=FALSE.

  • Set the field in public
    • This makes it so R6 knows about the key that is going to be set later
  • Set the value in the init method
    • This makes a new reactive for each new R6 object.

My standard of practice is to set default atomic values in public and for anything that is a structure, default the value with a <CLASS> string and set the value in the init method. While not true values, it provides a placeholder until they are initialized and helps me remember what type the values are.

Ex:

testBaseModel <- R6Class(
  "testBaseModel",
  public = list(
    nonReactiveVar = FALSE,
    reactiveVar = "<reactiveValues>",
    initialize = function() {
      self$reactiveVar = reactiveValues(bTest = FALSE)
      self
    }
  )
)

Thanks for the suggestion, it works nicely without unlocking the environment.

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.