R Shiny中renderPlot在for循环中不遵循父环境的解决方法
解决Shiny中for循环生成renderPlot重复的问题
这是R的闭包延迟绑定导致的问题:循环中创建的renderPlot闭包并没有立即捕获code变量的当前值,而是保存了对变量的引用。当这些renderPlot实际执行时,循环已经结束,code变量的值固定为最后一个元素plot3,因此所有图形都显示相同标题。
以下是两种可行的解决方案:
方案一:用局部变量捕获当前值
在循环内部新增一个局部变量,捕获当前迭代的code值,让闭包绑定这个固定值而非变量引用:
library("shiny") plotNames <- c("plot1", "plot2","plot3") ui <- basicPage(uiOutput("all")) server <- function(input, output,session) { prepGraph <- reactiveVal(NULL) output$all <- renderUI({ items <- list() for(iCode in 1:length(plotNames)){ items[[iCode]] <- plotOutput(plotNames[iCode]) } for(code in plotNames){ cat("\ntestParent:",code) # 用局部变量固定当前code的值 current_code <- code output[[code]] <- renderPlot({ cat("\ntestRender:",current_code) plot(1,main=current_code) }) } prepGraph(TRUE) return(do.call(tagList,items)) }) } test <- shinyApp(ui, server)
方案二:用lapply替代for循环
lapply的每个迭代都会生成独立的执行环境,自动捕获当前迭代的变量值,从根源避免闭包引用问题:
library("shiny") plotNames <- c("plot1", "plot2","plot3") ui <- basicPage(uiOutput("all")) server <- function(input, output,session) { prepGraph <- reactiveVal(NULL) output$all <- renderUI({ # 用lapply生成plotOutput列表 items <- lapply(plotNames, function(name) plotOutput(name)) # 用lapply循环创建renderPlot lapply(plotNames, function(code) { cat("\ntestParent:",code) output[[code]] <- renderPlot({ cat("\ntestRender:",code) plot(1,main=code) }) }) prepGraph(TRUE) return(do.call(tagList,items)) }) } test <- shinyApp(ui, server)
修改后运行代码,命令行将输出对应不同名称的testRender日志,界面会生成三个标题分别为plot1、plot2、plot3的独立图形。
内容的提问来源于stack exchange,提问作者cmbarbu
相关产品推荐
相关产品推荐

