I'm having an issue with reactivity when using modules in R. If I update a module and then try to update another module with those updated values, I instead get the values prior to the update.
I've written up some basic code to show what I mean below. Here I have an app that updates a rHandsontableOutput
placed in a module called my_module
and then copies this updated rHandsontableOutput
to a second module called module_to_update
when a button is pressed.
What I'm finding is that the first table in my_module
will update but not the one in module_to_update
. Instead, the module_to_update
table will receive a copy of my_module
's initial table prior to the update. If I press the update button again, things work as expected.
I'm guessing this is an issue with either how I'm handling the session or reactive values generally, but I'm out of ideas.
QUESTION: How can I set up reactive values and modules such that I can run operations on updated module data within the same function call? (e.g. see the observeEvent(input$update_btn, ...)
call below for an example)
application.R
library(shiny)
library(rhandsontable)
source('my_modules.R')
active_tab = ""
ui <- navbarPage("Module Test Tool",
tabsetPanel(id = 'mainTabset',
tabPanel("My Tab",
#This is the HoT that works as expected, updating when called upon
h4("Table 1"),
myModuleUI('my_module'),
#This is the HoT that does NOT work as expected. This HoT fails to use the updated values from 'my_module' HoT
h4("Table to be updated"),
myModuleUI('module_to_update'),
br(),
br(),
fluidRow(
#this button updates tables to new values
actionButton("update_btn", "Update and Add Tables"),
br(),
br(),
textOutput('table1_sum'),
textOutput('table2_sum'),
br(),
br()
)
)
)
)
server <- function(input, output, session) {
#Link logic for tab module
callModule(myModule, 'my_module')
#This button sums up all the rHandsonTable data frames
observeEvent(input$update_btn, {
#Update values in table and integer drop down list before doing basic operations on them
#New values should be all 5s
five_col = rep(5,3)
callModule(updateModule, 'my_module', 5, data.frame(col1 = five_col,
col2 = five_col,
col3 = five_col))
#Grabs updated module table and does operations on it
module_data = callModule(getMyModuleData, 'my_module')
module_int= module_data$module_int
module_df = module_data$module_df
output$table1_sum = renderText({
paste0("Sum of Table 1 is: ", sum(module_df())," | The selected integer is: ", module_int())
})
#------------------------------------------------------
#------------------ERROR BELOW-------------------------
#------------------------------------------------------
#THIS IS THE CODE THAT FAILS. This updates a 2nd module that should mirror the updated values. However, this results in old values.
callModule(updateModule, 'module_to_update', module_int(), module_df())
#Tries to call on new, updated table
updated_module_data = callModule(getMyModuleData, 'module_to_update')
updated_module_int= updated_module_data$module_int
updated_module_df = updated_module_data$module_df
#Display results of basic operations on new table
output$table2_sum = renderText({
paste0("Sum of Updated Table is: ", sum(updated_module_df())," | The selected integer is: ", updated_module_int())
})
})
}
## Create Shiny app ----
shinyApp(ui, server)
my_modules.R
#Simple module containing one rHandsontable and a drop down list of integers
myModuleUI <- function(id,tab_name){
ns <- NS(id)
fluidRow(
rHandsontableOutput(ns("module_hot")),
selectInput(ns('module_int_list'),"Integers:",c(1:5), selected = 1)
)
}
#Initializes myModuleUI rHandsonTable with some values
myModule <- function(input, output, session) {
one_col = rep.int('VALUE AT INITIALIZATION',3)
df = data.frame(col1 = one_col,
col2 = one_col,
col3 = one_col)
output$module_hot <- renderRHandsontable({
rhandsontable(df, stretchH = "none", rowHeaders = NULL)
})
}
#Returns myModule data for use outside of the module
getMyModuleData <- function(input,output,session){
return (
list(
module_df = reactive({hot_to_r(input$module_hot)}),
module_int = reactive({input$module_int_list})
)
)
}
updateModule<- function(input,output,session, new_integer, new_dataframe){
if(!is.null(new_dataframe))
{
output$module_hot <- renderRHandsontable({
rhandsontable(new_dataframe, stretchH = "none", rowHeaders = NULL)
})
}
outputOptions(output, "module_hot", suspendWhenHidden = FALSE)
updateSelectInput(session, "module_int_list",selected = new_integer)
}