简体   繁体   English

R Shiny模块在同一事件中未响应更新

[英]R Shiny module not updating reactively within same event

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. 在R中使用模块时, 我的反应性存在问题。 如果我更新一个模块,然后尝试使用这些更新的值更新另一个模块,我会在更新之前获取这些值。

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. 在这里,我有一个应用程序,该应用程序更新放置在名为my_module的模块中的rHandsontableOutput ,然后在按下按钮时将此更新的rHandsontableOutput复制到第二个模块module_to_update

What I'm finding is that the first table in my_module will update but not the one in module_to_update . 我发现的是my_module中的第一个表将更新,而module_to_update中的第一个表不会更新。 Instead, the module_to_update table will receive a copy of my_module 's initial table prior to the update . 相反, module_to_update表将在更新之前收到my_module初始表的副本。 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? 问题 :如何设置反应值和模块,以便可以在同一函数调用中对更新的模块数据运行操作? (eg see the observeEvent(input$update_btn, ...) call below for an example) (例如,请参见下面的observeEvent(input$update_btn, ...)调用)

Image: 图片: 更新的表未复制

application.R 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 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)
}


There are a few problems in here... 这里有一些问题...

You are calling multiple different modules with the same namespace. 您正在使用相同的名称空间调用多个不同的模块。 Modules are supposed to operate independently of each other. 模块应该彼此独立运行。 They should each have their own namespace. 它们每个都应具有自己的名称空间。 The following are not correct: 以下是不正确的:

callModule(myModule, 'my_module') 

callModule(updateModule, 'my_module', 5, data.frame(col1 = five_col,
                                                          col2 = five_col,
                                                          col3 = five_col))

module_data = callModule(getMyModuleData, 'my_module')

You are calling modules from within observeEvent() . 您正在从observeEvent()中调用模块。 This means every time you observe that event you try to initialize that module. 这意味着每次观察到该事件时,您都会尝试初始化该模块。 You don't want to initialize the module, you want to pass the new variables to that module. 您不想初始化模块,而是想将新变量传递给该模块。 If you make a module return it's values, then use those returned values as inputs into another module you won't need to observe the event...the module that receives the new information will decide whether to observe the change. 如果使模块返回其值,则将这些返回的值用作另一个模块的输入,您将不需要观察该事件……接收新信息的模块将决定是否观察该变化。

You have created a function/module getMyModuleData that is only supposed to return data that is present in a different module. 您已经创建了一个函数/模块getMyModuleData ,该函数/模块仅应返回存在于其他模块中的数据。 Instead you should have the other module return the data you want. 相反,您应该让另一个模块返回所需的数据。

Check out: https://shiny.rstudio.com/articles/communicate-bet-modules.html . 检出: https : //shiny.rstudio.com/articles/communicate-bet-modules.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM