You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让R Shiny模态框中的数据在渲染前一致更新

解决R Shiny模态框数据刷新问题

问题描述

我尝试让R Shiny应用模态框中的数据(output$selected_table和output$selected_details)始终在渲染前刷新,但应用有时有效、经常失效。尤其是在模态框左侧表格(output$selected_table)选择不同行后,关闭模态框再选择output$summary_table的另一行重新打开时,之前的图表会短暂可见。

问题原因

  1. 模态框关闭时,模块内的反应式状态(如表格选中行)未被重置,再次打开时会沿用之前的状态。
  2. getReactableState获取的表格选中状态未与当前选中的物种关联,切换物种时不会自动重置。
  3. outputOptions(output, ..., suspendWhenHidden = FALSE)放在render函数内部,执行时机不当,可能导致输出无法正确保持激活状态。

解决方案

1. 重置模块内反应式状态

在details_server中监听selected_species的变化,切换物种时强制重置selected_table的选中行,更新反应式状态。

2. 调整outputOptions位置

将outputOptions移至render函数外部,确保模块初始化时就设置输出的suspendWhenHidden属性。

3. 强化反应式依赖

让selected_details_row等反应式对象依赖于当前的selected_species,避免沿用旧数据。

修改后的完整代码

library(shiny)
library(dplyr)
library(ggplot2)
library(reactable)

iris_data = iris
iris_summary <- iris_data %>% group_by(Species) %>% summarise_all(mean)

summary_ui <- function(id) {
  ns = NS(id)
  reactableOutput(ns("summary_table"))
}

details_ui <- function(id) {
  ns = NS(id)
  fluidPage(
    fluidRow(
      column(6, reactableOutput(ns("selected_table"))),
      uiOutput(ns("selected_details"))
    )
  )
}

details_server <- function(id, summary_data, full_data, selected_summary_row) {
  moduleServer(id, function(input, output, session) {
    ns <- session$ns
    
    # 提前设置output的suspendWhenHidden属性
    outputOptions(output, "selected_table", suspendWhenHidden = FALSE)
    outputOptions(output, "selected_details_plot", suspendWhenHidden = FALSE)
    outputOptions(output, "selected_details_table", suspendWhenHidden = FALSE)
    outputOptions(output, "selected_details", suspendWhenHidden = FALSE)
    
    selected_species <- reactive({
      req(selected_summary_row() > 0)
      summary_data[selected_summary_row(), ]$Species
    })
    
    selected_data <- reactive({
      req(selected_summary_row() > 0)
      full_data %>% filter(Species == selected_species()) 
    })
    
    output$selected_table <- renderReactable({
      reactable(
        selected_data(), 
        selection = "single", 
        onClick = "select",
        defaultSelected = 1
      )
    })
    
    # 监听selected_species变化,重置表格选中行
    observeEvent(selected_species(), {
      updateReactable("selected_table", selected = 1)
    })
    
    selected_details_row = reactive({
      req(selected_species()) # 添加依赖,确保物种变化时更新
      getReactableState("selected_table", "selected") %||% 1
    })
    
    toggle_plot = reactive({
      req(selected_details_row())
      selected_details_row() %% 2 == 0
    })
    
    selected_details_data = reactive({
      req(selected_data(), selected_details_row())
      selected_data()[selected_details_row(), ]
    })
    
    output$selected_details_plot <- renderPlot({
      req(toggle_plot(), selected_details_data())
      selected_details_data() %>%
        ggplot(aes(x = Sepal.Length, y = Sepal.Width)) +
        geom_point()
    }, width = 500, height = 500)
    
    output$selected_details_table <- renderReactable({
      req(!toggle_plot(), selected_details_data())
      reactable(selected_details_data())
    })
    
    output$selected_details = renderUI({
      req(toggle_plot())
      if (toggle_plot()) {
        column(6, plotOutput(ns("selected_details_plot")))
      } else {
        column(6, reactableOutput(ns("selected_details_table")))
      }
    })
  })
}

summary_server <- function(id, full_data, summary_data) {
  moduleServer(id, function(input, output, session) {
    ns <- session$ns
    
    output$summary_table <- renderReactable({
      reactable(
        summary_data, 
        selection = "single", 
        onClick = "select"
      )
    })
    
    selected_summary_row = reactive(getReactableState("summary_table", "selected"))
    
    # 关闭模态框时清空主表格选中状态(可选增强)
    observeEvent(input$modal, {
      if (is.null(input$modal)) {
        updateReactable("summary_table", selected = NULL)
      }
    }, ignoreNULL = FALSE)
    
    observeEvent(selected_summary_row(), {
      req(selected_summary_row() > 0)
      showModal(modalDialog(
        details_ui(ns("details")),
        easyClose = TRUE,
        id = ns("modal") # 给模态框设置id,以便监听关闭事件
      ))
    })
    
    details_server("details", summary_data, full_data, selected_summary_row)
  })
}

ui <- fluidPage(
  tags$head(tags$style(".modal-dialog{ width: 60% }")),
  tags$head(tags$style(".modal-body{ min-height: 600px }")),
  titlePanel("Iris Dataset"),
  sidebarLayout(
    sidebarPanel(),
    mainPanel(
      summary_ui("summary")
    )
  )
)

server <- function(input, output, session) {
  summary_server("summary", iris_data, iris_summary)
}

shinyApp(ui, server)

关键修改点说明

  • 将outputOptions移至模块初始化阶段,避免重复执行。
  • 给模态框添加id,监听关闭事件以清空主表格选中状态(可选增强)。
  • 在selected_details_row中添加selected_species作为依赖,确保物种切换时状态更新。
  • 使用updateReactable在物种切换时强制重置selected_table的选中行为第一行。
  • 优化req语句,确保所有反应式输出都依赖于最新的数据源。

内容的提问来源于stack exchange,提问作者tauft

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 12:14:54