Why does accessing a reactive sometimes need two brackets ()()

I have created a module which takes a data.frame and displays it in a DTOutput. When the user changes the selected row it returns the row number of that row. If I use a reactive data.frame as the input to my module, then to access the returned value I seem to have to use two pairs of brackets. I am sure this should not be necessary, but can't seem to find any other way of doing it. Where am I going wrong?
This is my module:

library(shiny)
library(DT)

#' Datatable output for displaying transactions
#'
#' @param id Namespace (char)
#'
#' @return fluidRow ui containing DTOutput
transactions_ui <- function(id){
  fluidRow(
    DT::DTOutput(
      NS(id, "DT_transactions")
    )
  )
}

#' transaction module server-side processing
#'
#' @param id character Module Namespace id
#' @param transactions_df data.frame containing transaction details
#'
#' @return list
#'  "selected_row" the currently selected row (int)
#'  "transactions_df" the transactions data.frame
transactions_server <- function(id, transactions_df){
  moduleServer(id, function(input, output, session){
    
    reactive_row_selected <- reactiveVal(1)
    output$DT_transactions <- DT::renderDT(
      {
        datatable(
          data = transactions_df,
          editable = TRUE,
          selection = list(mode = "single", selected = reactive_row_selected())
        )
      }
    )
  
  reactive({
    req(nrow(transactions_df)>0)
    input$DT_transactions_rows_selected
  })
  
  })
}

And this is my testing function:

library(tibble)
transactions_demo <- function(){
  datafile <- "test.rds"
  reactive_transactions_df <- reactiveVal(new_tibble(list("Category" = c("A", "B"))))
  ui <- fluidPage(
    transactions_ui("current"),
    fluidRow(
      actionButton("change_value", "Change")
    )
  )
  server <- function(input, output, session){
      
    sel_row <- reactiveVal()
    
    observeEvent(
      reactive_transactions_df(),
      sel_row(transactions_server("current", transactions_df = reactive_transactions_df()))
    )

    observeEvent(
      input$change_value,{
        print(sel_row()()) # why do I need 2 brackets here?
        df <- reactive_transactions_df()
        df[sel_row()(),]$Category <- c("C")
        reactive_transactions_df(df)
      }
    )
  }
  shinyApp(ui, server)
}

It achieves what I want I just don't understand why I need the double brackets ()() to accesss the return value from the module.

The reason you need two parenthesis in your example is because you are returning a reactive from the module into another reactive, “sel_row”. When you access the value of sel_row with sel_row() what you get is another reactive which then needs the additional parenthesis. What made this a little odd was the fact that you had

observeEvent(
      reactive_transactions_df(),
      sel_row(transactions_server("current", transactions_df = reactive_transactions_df()))
    )

For shiny modules, it’s not necessary to wrap the call to the module in an observe. My guess is you did that because you needed reactive_transactions_df to trigger the module. Instead, I think it’s best practice to pass the reactive object to the module, and not the value inside the reactive. In other words, transactions_df = reactive_transactions_df() should be transactions_df = reactive_transactions_df. Then in your module, add in the parenthesis to the transactions_df object throughout. Lastly, we can omit the sel_row <- reactiveVal() initializer because the module will return a reactive, we just can assign it there.

Please see the app below and let me know if it works as you would expect (understanding this is a toy example). FYI I changed the "C" assignment to something random to test.

library(shiny)
library(DT)

#' Datatable output for displaying transactions
#'
#' @param id Namespace (char)
#'
#' @return fluidRow ui containing DTOutput
transactions_ui <- function(id){
  fluidRow(
    DT::DTOutput(
      NS(id, "DT_transactions")
    )
  )
}

#' transaction module server-side processing
#'
#' @param id character Module Namespace id
#' @param transactions_df reactive data.frame containing transaction details
#'
#' @return list
#'  "selected_row" the currently selected row (int)
#'  "transactions_df" the transactions reactive data.frame
transactions_server <- function(id, transactions_df){
  moduleServer(id, function(input, output, session){
    
    reactive_row_selected <- reactiveVal(1)
    output$DT_transactions <- DT::renderDT(
      {
        datatable(
          data = transactions_df(), # add '()'
          editable = TRUE,
          selection = list(mode = "single", selected = reactive_row_selected())
        )
      }
    )
    
    reactive({
      req(nrow(transactions_df())>0) # add '()'
      input$DT_transactions_rows_selected
    })
    
  })
}

library(tibble)
transactions_demo <- function(){
  datafile <- "test.rds"
  reactive_transactions_df <- reactiveVal(new_tibble(list("Category" = c("A", "B"))))
  ui <- fluidPage(
    transactions_ui("current"),
    fluidRow(
      actionButton("change_value", "Change")
    )
  )
  server <- function(input, output, session){
    
    # Module call here, returning a reactive
    sel_row <- transactions_server("current", 
                                   transactions_df = reactive_transactions_df # remove '()'
                                   ) 
    
    observeEvent(
      input$change_value,{
        print(sel_row()) # one parenthesis
        df <- reactive_transactions_df()
        df[sel_row(),]$Category <- c(sample(LETTERS,size = 1,replace = T)) # one parenthesis
        reactive_transactions_df(df)
      }
    )
  }
  shinyApp(ui, server)
}
1 Like

Thanks for that that is what I wanted. So I pass in a reactive object, no parentheses. The access it's value using parentheses.
Brilliant.

Regards,

Jon

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

If you have a query related to it or one of the replies, start a new topic and refer back with a link.